Spaces:
Build error
Build error
| import os | |
| import json | |
| from copy import deepcopy | |
| import re | |
| import ast | |
| import numpy as np | |
| from scipy.spatial.transform import Rotation as R | |
| import uuid | |
| import xml.etree.ElementTree as ET | |
| BLOCKPROPERTYPATH=R"Besiege_blocks_markov.json" | |
| FLIP_SENSITIVE_BLOCKS = ["2","46"] | |
| def are_quaternions_similar(q1, angle_threshold=1e-3): | |
| q2 = np.array([0, -0.7071068, 0, 0.7071068]) | |
| # 将四元数转换为旋转对象 | |
| r1 = R.from_quat(q1) | |
| r2 = R.from_quat(q2) | |
| # 计算两个旋转之间的夹角差异 | |
| relative_rotation = r1.inv() * r2 | |
| angle = relative_rotation.magnitude() | |
| # 如果角度差异小于阈值,则认为两个四元数大致相同 | |
| return angle < angle_threshold | |
| def generate_guid(): | |
| """生成一个唯一的GUID""" | |
| return str(uuid.uuid4()) | |
| def add_rotations(q1, q2): | |
| """叠加两个旋转四元数""" | |
| r1 = R.from_quat(q1) # 从四元数创建旋转对象 | |
| r2 = R.from_quat(q2) # 从四元数创建旋转对象 | |
| r_combined = r1 * r2 # 叠加旋转 | |
| return r_combined.as_quat() # 返回四元数 (xyzw 格式) | |
| def read_txt(path): | |
| """读取文本文件内容""" | |
| with open(path, "r", encoding="utf-8") as file: | |
| return file.read() | |
| def write_file(path, content): | |
| """将内容写入文本文件""" | |
| with open(path, 'w', encoding='utf-8') as file: | |
| file.write(content) | |
| def extract_json_from_string(input_string: str,return_raw_str=False): | |
| if isinstance(input_string,list): | |
| return input_string | |
| if os.path.exists(input_string): | |
| input_content = read_txt(input_string) | |
| else: | |
| input_content = deepcopy(input_string) | |
| match = re.search(r"```json(.*?)```", input_content, re.DOTALL) | |
| if match: | |
| json_content = match.group(1).strip() | |
| try: | |
| if return_raw_str: | |
| return json.loads(json_content),json_content | |
| return json.loads(json_content) | |
| except json.JSONDecodeError: | |
| pass | |
| try: | |
| if return_raw_str: | |
| return json.loads(input_content),input_content | |
| return json.loads(input_content) | |
| except json.JSONDecodeError: | |
| try: | |
| if return_raw_str: | |
| return json.loads(input_content),input_content | |
| return ast.literal_eval(input_content) | |
| except (ValueError, SyntaxError): | |
| if return_raw_str: | |
| return None,"" | |
| return None | |
| def get_relative_pos_list(bp_oldpos,ref_p,ref_r,scale=1,decimals=None): | |
| bp_newpos = [] | |
| if ref_r.shape[0] != 3 or ref_r.shape[1] != 3: | |
| ref_r = R.from_quat(ref_r).as_matrix() # 如果是四元数,转换为旋转矩阵 | |
| for point in bp_oldpos: | |
| point_lp = ref_p+np.dot(ref_r, point*scale) | |
| bp_newpos.append(tuple(point_lp)) | |
| bp_newpos = np.array(bp_newpos)#可建造点局部位置 | |
| if decimals!=None: | |
| bp_newpos = np.round(bp_newpos,decimals=decimals) | |
| return bp_newpos | |
| def get_bbox(manu_lp,manu_lr,scale,bc_gc,bbox_size,gp,gr): | |
| if manu_lr.shape[0] != 3 or manu_lr.shape[1] != 3: | |
| manu_lr = R.from_quat(manu_lr).as_matrix() | |
| if gr.shape[0] != 3 or gr.shape[1] != 3: | |
| gr = R.from_quat(gr).as_matrix() | |
| half_bbox_size = np.array(bbox_size) / 2.0 | |
| bbox_lp = [] | |
| for z in [-1, 1]: | |
| for x in [-1, 1]: | |
| for y in [-1, 1]: | |
| point = (manu_lp+bc_gc) + (x * half_bbox_size[0], y * half_bbox_size[1], z * half_bbox_size[2]) | |
| bc_point = point-manu_lp | |
| point_lp = manu_lp+np.dot(manu_lr, bc_point*scale) | |
| bbox_lp.append(tuple(point_lp)) | |
| bbox_lp = np.array(bbox_lp)#碰撞盒顶点相对位置 | |
| bbox_gp = get_relative_pos_list(bbox_lp,gp,gr,decimals=2) | |
| return bbox_lp,bbox_gp | |
| def compute_normal_vector(vertices, bp): | |
| bp = np.round(bp, decimals=3) | |
| # 计算每个维度的最小和最大坐标值 | |
| min_coords = np.min(vertices, axis=0) | |
| max_coords = np.max(vertices, axis=0) | |
| # 初始化法向量 | |
| normal = np.zeros(3) | |
| # epsilon = 5e-4 # 容差范围,还算严格 | |
| epsilon = 0.005 # 容差范围 | |
| # 检查点是否在x方向的面上 | |
| if abs(bp[0] - min_coords[0]) < epsilon: | |
| normal = np.array([-1, 0, 0]) # 左面法向量 | |
| elif abs(bp[0] - max_coords[0]) < epsilon: | |
| normal = np.array([1, 0, 0]) # 右面法向量 | |
| # 检查y方向 | |
| elif abs(bp[1] - min_coords[1]) < epsilon: | |
| normal = np.array([0, -1, 0]) # 下面法向量 | |
| elif abs(bp[1] - max_coords[1]) < epsilon: | |
| normal = np.array([0, 1, 0]) # 上面法向量 | |
| # 检查z方向 | |
| elif abs(bp[2] - min_coords[2]) < epsilon: | |
| normal = np.array([0, 0, -1]) # 后面法向量 | |
| elif abs(bp[2] - max_coords[2]) < epsilon: | |
| normal = np.array([0, 0, 1]) # 前面法向量 | |
| else: | |
| raise ValueError("点不在长方体的任何一个面上") | |
| return normal | |
| def get_mybuildingpoints(bc_bp,manu_lp,manu_lr,gp,gr,bc_gc,bbox_size,scale=1): | |
| bp_ori = np.array(bc_bp) | |
| bp_lp = get_relative_pos_list(bp_ori,manu_lp,manu_lr,scale=scale) | |
| bp_gp = get_relative_pos_list(bp_lp,gp,gr,decimals=2) | |
| bbox_lp,bbox_gp = get_bbox(manu_lp,manu_lr,scale,bc_gc,bbox_size,gp,gr) | |
| my_building_points = bp_gp.copy() | |
| my_building_points_buildrotation=[] | |
| # print(f"bp_gp:{bp_gp}") | |
| for i in range(len(my_building_points)): | |
| # print(f"bp_lp:{bp_lp[i]}") | |
| # print(f"bbox_lp:{bbox_lp}") | |
| normal_vector_l = compute_normal_vector(bbox_lp,bp_lp[i]) | |
| rotated_initvec = np.array([0,0,1]) | |
| building_points_rot_quat = rotation_quaternion(rotated_initvec,normal_vector_l) | |
| my_building_points_buildrotation.append(building_points_rot_quat) #这个是对的 | |
| my_building_points_buildrotation = np.array(my_building_points_buildrotation) | |
| return my_building_points,my_building_points_buildrotation | |
| def rotation_quaternion(v_from, v_to): | |
| """计算从 v_from 到 v_to 的旋转四元数 (xyzw 格式)""" | |
| v_from = v_from / np.linalg.norm(v_from) # 单位化 | |
| v_to = v_to / np.linalg.norm(v_to) # 单位化 | |
| # 计算旋转轴和旋转角 | |
| cross = np.cross(v_from, v_to) | |
| dot = np.dot(v_from, v_to) | |
| if np.allclose(cross, 0) and np.allclose(dot, 1): | |
| return np.array([0, 0, 0, 1]) # 无需旋转 | |
| elif np.allclose(cross, 0) and np.allclose(dot, -1): | |
| # 特殊情况:v_from 和 v_to 反方向 | |
| # 选择一个垂直于 v_from 的轴作为旋转轴 | |
| if np.isclose(v_from[0], 0) and np.isclose(v_from[1], 0): | |
| axis = np.array([0, 1, 0]) # 如果 v_from 在 z 轴上,选择 y 轴作为旋转轴 | |
| else: | |
| axis = np.cross(v_from, np.array([0, 1, 0])) # 选择垂直于 v_from 和 y 轴的向量 | |
| axis = axis / np.linalg.norm(axis) | |
| angle = np.pi | |
| else: | |
| angle = np.arccos(dot) | |
| axis = cross / np.linalg.norm(cross) | |
| # 生成四元数 | |
| q = R.from_rotvec(axis * angle).as_quat() # xyzw格式 | |
| return q | |
| def format_json(input_json): | |
| try: | |
| new_clean_json=[] | |
| for json_info in input_json: | |
| new_clean_dict={} | |
| if int(json_info["id"]) not in [7,9]: | |
| new_clean_dict["id"]=str(json_info["id"]) | |
| new_clean_dict["order_id"]=int(json_info["order_id"]) | |
| new_clean_dict["parent"]=int(json_info["parent"]) | |
| new_clean_dict["bp_id"]=int(json_info["bp_id"]) | |
| else: | |
| new_clean_dict["id"]=str(json_info["id"]) | |
| new_clean_dict["order_id"]=int(json_info["order_id"]) | |
| new_clean_dict["parent_a"]=int(json_info["parent_a"]) | |
| new_clean_dict["bp_id_a"]=int(json_info["bp_id_a"]) | |
| new_clean_dict["parent_b"]=int(json_info["parent_b"]) | |
| new_clean_dict["bp_id_b"]=int(json_info["bp_id_b"]) | |
| new_clean_json.append(new_clean_dict) | |
| return new_clean_json | |
| except: | |
| return input_json | |
| def convert_to_numpy(data): | |
| no_globalrt = True | |
| for info in data: | |
| if "GlobalPosition" in info: | |
| info["GlobalPosition"] = np.array(info["GlobalPosition"]) | |
| info["GlobalRotation"] = np.array(info["GlobalRotation"]) | |
| no_globalrt = False | |
| else: | |
| keys_to_convert = ["corners", "building_center", "scale","manu_lr","manu_lp","bp_lr"] | |
| for key in keys_to_convert: | |
| if key in info: | |
| info[key] = np.array(info[key]) | |
| new_data = [{"GlobalPosition":np.array([0,5.05,0]),"GlobalRotation":np.array([0,0,0,1])}] | |
| if no_globalrt: | |
| new_data.extend(data) | |
| return new_data | |
| return data # 返回原始数据 | |
| def get_3d_from_llm(block_sizes, input_info, gp, gr, log=False): | |
| info = deepcopy(input_info) | |
| for block in info: | |
| order_id = int(block["order_id"]) | |
| block_id = str(block["id"]) | |
| # Handle scale | |
| if "scale" not in block: | |
| block["scale"] = np.array([1,1,1]) | |
| else: | |
| print(f"警告!{order_id}改变了scale!使用初始值") | |
| block["scale"] = np.array([1,1,1]) | |
| # Handle rotations | |
| if "bp_lr" not in block: | |
| if "manu_lr" not in block: | |
| block["bp_lr"] = np.array([0,0,0,1]) | |
| elif "manu_lr" in block and str(block.get("parent", "")) not in ("-1", ""): | |
| print(f"警告!{order_id}有manu_lr但不是根节点!旋转使用初始值") | |
| block["bp_lr"] = np.array([0,0,0,1]) | |
| block.pop("manu_lr", None) | |
| block_info = block_sizes[block_id] | |
| parent = int(block.get("parent", -1)) | |
| # Handle parent cases | |
| if parent == -1: | |
| if block_id not in ("0","7", "9"): | |
| print("警告!发现了非起始方块的无父节点块") | |
| if block_id in ("7", "9"): | |
| parent_a, parent_b = int(block["parent_a"]), int(block["parent_b"]) | |
| bp_id_a, bp_id_b = int(block["bp_id_a"]), int(block["bp_id_b"]) | |
| block["bp_lr"] = np.array([0,0,0,1]) | |
| block["manu_lr"] = add_rotations( | |
| info[parent_a]["my_building_points_buildrotation"][bp_id_a], | |
| block["bp_lr"] | |
| ) | |
| block["manu_lp_a"] = info[parent_a]["my_building_points"][bp_id_a] - gp | |
| block["manu_lp_b"] = info[parent_b]["my_building_points"][bp_id_b] - gp | |
| else: | |
| if "manu_lr" not in block: | |
| block["manu_lr"] = np.array([0,0,0,1]) | |
| block["manu_lp"] = np.array([0,0,0]) | |
| else: | |
| print("警告!发现了某个方块的manu_lr和manu_lp") | |
| if block["manu_lr"].shape != (3, 3): | |
| block["manu_lr"] = R.from_matrix(block["manu_lr"]).as_quat() | |
| else: | |
| try: | |
| bp_id = int(block["bp_id"]) | |
| parent_rot = info[parent]["my_building_points_buildrotation"][bp_id] | |
| block["manu_lr"] = add_rotations(parent_rot, block["bp_lr"]) | |
| block["manu_lp"] = info[parent]["my_building_points"][bp_id] - gp | |
| except Exception: | |
| print(f"警告!parent:{parent},order_id{order_id}的my_building_points或my_building_points_buildrotation不存在") | |
| # print(info[parent]) | |
| pass | |
| if block_id not in ("7", "9"): | |
| if block_id in FLIP_SENSITIVE_BLOCKS: | |
| block["flip"] = are_quaternions_similar(block["manu_lr"]) | |
| bc_bp = block_info['bc_bp'] | |
| bc_gc = block_info['bc_gc'] | |
| bbox_size = block_info['bbox_size'] | |
| if block_id == "30": | |
| bc_gc = [0,0,0.5] | |
| bbox_size = [1,1,1] | |
| building_points, build_rotation = get_mybuildingpoints( | |
| bc_bp, block["manu_lp"], block["manu_lr"], gp, gr, | |
| bc_gc, bbox_size, scale=block["scale"] | |
| ) | |
| block["my_building_points"] = building_points | |
| block["my_building_points_buildrotation"] = build_rotation | |
| if log: | |
| print(f"block_id:{block_id}\nscale:{block['scale']}\nbc_gc:{bc_gc}\n" | |
| f"bbox_size:{bbox_size}\nmanu_lp:{block['manu_lp']}\n" | |
| f"manu_lr:{block['manu_lr']}\nmy_building_points:{building_points}\n" | |
| f"my_building_points_buildrotation:{build_rotation}") | |
| return info | |
| def llm2xml_filetree(block_details, block_sizes_path, selected_menu=None): | |
| with open(block_sizes_path, 'r', encoding='utf-8') as file: | |
| block_sizes = json.load(file) | |
| global_rt = block_details.pop(0) | |
| gp, gr_quat = global_rt["GlobalPosition"], global_rt["GlobalRotation"] | |
| gr_matrix = R.from_quat(gr_quat).as_matrix() | |
| blocks_to_delete = set() # 使用集合以避免重复添加和快速查找 | |
| blocks_to_delete_feedback = [] | |
| #先对block_details做一个整体格式检查 | |
| linear = {"id","order_id","parent_a", "bp_id_a", "parent_b", "bp_id_b"} | |
| non_linear = {"id","order_id", "parent", "bp_id"} | |
| for i, block in enumerate(block_details): | |
| if not (set(block.keys()) == linear or set(block.keys()) == non_linear): | |
| blocks_to_delete.add(i) | |
| blocks_to_delete_feedback.append( | |
| f"警告:块(orderID {i})结构非法" | |
| ) | |
| order_id_map = {int(b["order_id"]): b for b in block_details} # 方便快速查找父块 | |
| for i,block in enumerate(block_details): | |
| is_linear = False | |
| parent_order_a=-1 | |
| parent_order_b=-1 | |
| #检查一下value的格式 | |
| format_error = False | |
| for k,v in block.items(): | |
| if k =="id": | |
| if not isinstance(v,str): | |
| if isinstance(v,int): | |
| v = str(v) | |
| else: | |
| format_error = True | |
| if k in["order_id","parent_a", "bp_id_a", "parent_b", "bp_id_b", "parent", "bp_id"]: | |
| if not isinstance(v,int): | |
| if isinstance(v,str): | |
| try: | |
| v = int(v) | |
| except: | |
| format_error = True | |
| if format_error: | |
| blocks_to_delete.add(i) | |
| blocks_to_delete_feedback.append(f"警告:order{i}json格式非法") | |
| continue | |
| #先检查起始方块: | |
| if i==0: | |
| block_type = str(block["id"]) | |
| order_id = int(block["order_id"]) | |
| parent_order = int(block.get("parent", -2)) | |
| bp_id = int(block.get("bp_id", -2)) | |
| if any([block_type!="0",order_id!=0]): | |
| blocks_to_delete.add(i) | |
| blocks_to_delete_feedback.append(f"警告:起始方块非法") | |
| continue | |
| if any([parent_order!=-1,bp_id!=-1]): | |
| #起始方块不规范,parent bpid改成-1 -1 | |
| block["parent"]=-1 | |
| block["bp_id"]=-1 | |
| order_id = int(block["order_id"]) | |
| parent_order = int(block.get("parent", -1)) | |
| if parent_order==-1 and order_id!=0: | |
| is_linear = True | |
| parent_order_a = int(block.get("parent_a", -1)) | |
| parent_order_b = int(block.get("parent_b", -1)) | |
| parents = [parent_order_a,parent_order_b] | |
| else: | |
| parents = [parent_order] | |
| # 检查1: 父块是否已被标记为非法 | |
| if any(order in blocks_to_delete for order in parents): | |
| blocks_to_delete.add(order_id) | |
| blocks_to_delete_feedback.append(f"警告:块(orderID {order_id})的父块(orderID {parent_order})非法,因此也被标记为非法。") | |
| continue | |
| # 检查2: 父块的连接点(bp_id)是否有效 | |
| for i_th_parent,parent_order in enumerate(parents): | |
| parent_block = order_id_map.get(parent_order) | |
| if parent_block: | |
| parent_block_id = str(parent_block["id"]) | |
| if i_th_parent==0: | |
| bp_id = int(block.get("bp_id",block.get("bp_id_a",-1))) | |
| elif i_th_parent==1: | |
| bp_id = int(block.get("bp_id_b",-1)) | |
| else: | |
| bp_id=-1 | |
| if parent_block_id in block_sizes and bp_id >= len(block_sizes[parent_block_id]["bc_bp"]): | |
| blocks_to_delete.add(order_id) | |
| blocks_to_delete_feedback.append(f"警告:块(orderID {order_id})的父块(ID {parent_block_id})不存在可建造点{bp_id}。") | |
| continue | |
| # 检查3: 双父块(线性块)的特殊处理 | |
| if (not is_linear) and str(block.get("id")) in ["7", "9"]: | |
| blocks_to_delete.add(order_id) | |
| blocks_to_delete_feedback.append(f"警告:块(orderID {order_id})是线性块但不存在双parent属性。") | |
| continue | |
| elif is_linear and (str(block.get("id")) not in ["7", "9"]): | |
| blocks_to_delete.add(order_id) | |
| blocks_to_delete_feedback.append(f"警告:块(orderID {order_id})存在双parent属性但不是线性块。") | |
| continue | |
| # print(blocks_to_delete_feedback) | |
| if blocks_to_delete: | |
| # 从 block_details 中过滤掉要删除的块 | |
| block_details = [b for b in block_details if int(b["order_id"]) not in blocks_to_delete] | |
| # --- 计算 3D 位置并构建 XML 风格列表 --- | |
| processed_details = get_3d_from_llm(block_sizes, block_details, gp, gr_matrix, log=False) | |
| # print(block_details) | |
| xml_block_details = [{"GlobalPosition": gp, "GlobalRotation": gr_quat}] | |
| for block in processed_details: | |
| xml_info = { | |
| "id": block["id"], | |
| "order_id": block["order_id"], | |
| "guid": generate_guid() | |
| } | |
| if str(block["id"]) in ["7", "9"]: # 线性块 | |
| xml_info["Transform"] = {"Position": block["manu_lp_a"], "Rotation": np.array([0,0,0,1]), "Scale": block["scale"]} | |
| xml_info["end-position"] = block["manu_lp_b"] - block["manu_lp_a"] | |
| else: # 普通块 | |
| manu_lr = R.from_matrix(block["manu_lr"]).as_quat() if block["manu_lr"].shape == (3, 3) else block["manu_lr"] | |
| xml_info["Transform"] = {"Position": block["manu_lp"], "Rotation": manu_lr, "Scale": block["scale"]} | |
| if "flip" in block: # 轮子属性 | |
| xml_info.update({"flip": block["flip"], "auto": True, "autobrake": False}) | |
| if selected_menu and "special_props" in selected_menu: | |
| xml_info["WheelDoubleSpeed"] = "WheelDoubleSpeed" in selected_menu["special_props"] | |
| xml_block_details.append(xml_info) | |
| # print("\n".join(blocks_to_delete_feedback)) | |
| return xml_block_details, processed_details, "\n".join(blocks_to_delete_feedback) | |
| def facing(q_in): | |
| q_z_pos = np.array([0, 0, 0, 1]) | |
| q_z_neg = np.array([0, 1, 0, 0]) | |
| q_x_neg = np.array([0, -0.7071068, 0, 0.7071068]) | |
| q_x_pos = np.array([0, 0.7071068, 0, 0.7071068]) | |
| q_y_pos = np.array([-0.7071068,0, 0,0.7071068]) | |
| q_y_neg = np.array([0.7071068,0, 0,0.7071068]) | |
| angle_threshold = 1e-3 | |
| rots = [q_z_pos,q_z_neg,q_x_neg,q_x_pos,q_y_pos,q_y_neg] | |
| facing = ["z+","z-","x-","x+","y+","y-"] | |
| # 将四元数转换为旋转对象 | |
| r1 = R.from_quat(q_in) | |
| for q2 in range(len(rots)): | |
| r2 = R.from_quat(rots[q2]) | |
| # 计算两个旋转之间的夹角差异 | |
| relative_rotation = r1.inv() * r2 | |
| angle = relative_rotation.magnitude() | |
| # 如果角度差异小于阈值,则认为两个四元数大致相同 | |
| if(angle < angle_threshold): | |
| return facing[q2] | |
| return "Error!未找到正确方向" | |
| def check_overlap_or_connection(cube1, cube2): | |
| def get_bounds(vertices): | |
| x_min = min(v[0] for v in vertices) | |
| x_max = max(v[0] for v in vertices) | |
| y_min = min(v[1] for v in vertices) | |
| y_max = max(v[1] for v in vertices) | |
| z_min = min(v[2] for v in vertices) | |
| z_max = max(v[2] for v in vertices) | |
| return x_min, x_max, y_min, y_max, z_min, z_max | |
| x1_min, x1_max, y1_min, y1_max, z1_min, z1_max = get_bounds(cube1) | |
| x2_min, x2_max, y2_min, y2_max, z2_min, z2_max = get_bounds(cube2) | |
| if x1_max <= x2_min or x2_max <= x1_min: | |
| return False | |
| if y1_max <= y2_min or y2_max <= y1_min: | |
| return False | |
| if z1_max <= z2_min or z2_max <= z1_min: | |
| return False | |
| x_overlap = x1_min < x2_max and x2_min < x1_max | |
| y_overlap = y1_min < y2_max and y2_min < y1_max | |
| z_overlap = z1_min < z2_max and z2_min < z1_max | |
| return x_overlap and y_overlap and z_overlap | |
| def check_overlap(block_details,vis=True,corners_parent_llm_parent=None, | |
| language="zh"): | |
| def overlap_log(id1,id2): | |
| head1 = "方块order_id" | |
| head2 = "和方块order_id" | |
| overlap_head = "重叠" | |
| return f"{head1} {id1} {head2} {id2} {overlap_head}\n" | |
| overlaps = [] | |
| connections = [] | |
| # 检查每对方块是否重叠 | |
| overlaps_info="" | |
| # print(len(block_details)) | |
| # print(len(corners_parent_llm_parent)) | |
| for i in range(len(block_details)): | |
| # print(block_details[i]) | |
| for j in range(i + 1, len(block_details)): | |
| if "GlobalPosition" in block_details[i] or "GlobalPosition" in block_details[j]:continue | |
| # if np.all(block_details[i] == 0) or np.all(block_details[j] == 0): continue | |
| if "corners" in block_details[i] and "corners" in block_details[j]: | |
| corners1, id1 = (block_details[i]["corners"],i) | |
| corners2, id2 = (block_details[j]["corners"],j) | |
| else: | |
| corners1 = block_details[i] | |
| id1 = i | |
| corners2 = block_details[j] | |
| id2 = j | |
| #print(f"方块order_id {id1} 和方块order_id {id2}") | |
| results = check_overlap_or_connection(corners1, corners2) | |
| if results=="connected": | |
| #print(f"方块order_id {id1} 和方块order_id {id2} 相交") | |
| connections.append((id1, id2, corners1, corners2)) | |
| elif results: | |
| if corners_parent_llm_parent !=None: | |
| id1_type = str(corners_parent_llm_parent[id1][0]) | |
| id1_order = str(corners_parent_llm_parent[id1][1]) | |
| id1_parent_order = str(corners_parent_llm_parent[id1][2]) | |
| id2_type = str(corners_parent_llm_parent[id2][0]) | |
| id2_order = str(corners_parent_llm_parent[id2][1]) | |
| id2_parent_order = str(corners_parent_llm_parent[id2][2]) | |
| if id1_order==id2_parent_order: | |
| if str(id1_type)=="30":#如果1是2的父节点,并且1是容器 | |
| pass | |
| else: | |
| overlaps_info+=overlap_log(id1,id2) | |
| overlaps.append((id1, id2, corners1, corners2)) | |
| elif id2_order==id1_parent_order: | |
| if str(id2_type)=="30":#如果2是1的父节点,并且2是容器 | |
| pass | |
| else: | |
| overlaps_info+=overlap_log(id1,id2) | |
| overlaps.append((id1, id2, corners1, corners2)) | |
| else: | |
| overlaps_info+=overlap_log(id1,id2) | |
| overlaps.append((id1, id2, corners1, corners2)) | |
| else: | |
| overlaps_info+=overlap_log(id1,id2) | |
| overlaps.append((id1, id2, corners1, corners2)) | |
| if overlaps: | |
| # print(f"共发现 {len(overlaps)} 处重叠。") | |
| found_head = "共发现" | |
| overlaps_head="处重叠" | |
| overlaps_info+=f"{found_head} {len(overlaps)} {overlaps_head}\n" | |
| else: | |
| # print("没有重叠的方块。") | |
| overlaps_info+="没有错误" | |
| if vis: | |
| # 可视化结果 | |
| pass | |
| #print(overlaps_info) | |
| return overlaps_info | |
| def llm_feedback_3d(block_sizes, xml_block_details, block_details, autofit_gt=True, overlap_feedback=True, language="zh"): | |
| with open(block_sizes, 'r', encoding='utf-8') as file: | |
| block_sizes_content = json.load(file) | |
| gp, gr = xml_block_details[0]["GlobalPosition"], xml_block_details[0]["GlobalRotation"] | |
| corners_feedback_forquizzer = "块的3D信息:\n" | |
| corners_feedback_forbuilder = "块的朝向信息:\n" | |
| corners_parent_llm, corners_parent_llm_parent = [], [] | |
| for i, xml_block in enumerate(xml_block_details): | |
| if "GlobalPosition" in xml_block: continue | |
| block_id, order_id = xml_block["id"], xml_block["order_id"] | |
| if str(block_id) in ("7", "9"): | |
| corners_parent_llm_parent.append([block_id, order_id, -1]) | |
| corners_parent_llm.append(np.zeros((8,3))) | |
| continue | |
| x_transform = xml_block["Transform"] | |
| pos, rot, scale = x_transform["Position"], x_transform["Rotation"], x_transform["Scale"] | |
| # print(pos,rot,scale) | |
| block_info = block_sizes_content[str(block_id)] | |
| bbox_lp, bbox_gp = get_bbox(pos, rot, scale, block_info['bc_gc'], block_info['bbox_size'], gp, gr) | |
| corners_parent_llm.append(bbox_gp) | |
| corners_parent_llm_parent.append([block_id, order_id, block_details[i-1]["parent"]]) | |
| facing_dir = facing(rot) | |
| corners_feedback_forquizzer += f"order_id:{order_id}\n朝向:{facing_dir}\n块近似长方体顶点位置:{bbox_gp.tolist()}\n" | |
| corners_feedback_forbuilder += f"order_id:{order_id}\n朝向:{facing_dir}" | |
| # Calculate machine dimensions | |
| corners_arr = np.vstack([c for c in corners_parent_llm if c.size > 0]) | |
| min_vals, max_vals = corners_arr.min(axis=0), corners_arr.max(axis=0) | |
| lowest_y, highest_y = min_vals[1], max_vals[1] | |
| left_x, right_x = min_vals[0], max_vals[0] | |
| back_z, forward_z = min_vals[2], max_vals[2] | |
| geo_center = np.array([(right_x + left_x)/2, (highest_y + lowest_y)/2, (forward_z + back_z)/2]) | |
| if autofit_gt: | |
| xml_block_details[0]["GlobalPosition"][1] -= (lowest_y - 0.5) | |
| xml_block_details[0]["GlobalPosition"][0] -= geo_center[0] | |
| xml_block_details[0]["GlobalPosition"][2] -= geo_center[2] | |
| env_fail = (highest_y - lowest_y > 9.5) or (right_x - left_x > 17) or (forward_z - back_z > 17) | |
| height, wide, long = round(highest_y - lowest_y, 2), round(right_x - left_x, 2), round(forward_z - back_z, 2) | |
| # Validate machine structure | |
| machine_structure_error = "" | |
| if "corners" in block_details[1]: | |
| for i, block in enumerate(block_details): | |
| if "GlobalPosition" in block or str(block.get("id")) in ("7", "9"): continue | |
| if not np.allclose(block["corners"], corners_parent_llm[i], atol=1e-2): | |
| machine_structure_error += (f"order_id为{i}的方块的顶点信息不一致!\n" | |
| f"顶点信息:{block['corners']}\n" | |
| f"建造点相对信息反推的顶点信息:{corners_parent_llm[i]}\n") | |
| overlap_infos = check_overlap(corners_parent_llm, vis=False, corners_parent_llm_parent=corners_parent_llm_parent, language=language) if overlap_feedback else "重叠检查被屏蔽" | |
| return (corners_feedback_forquizzer, corners_feedback_forbuilder, env_fail, | |
| long, wide, height, machine_structure_error, overlap_infos) | |
| def create_xml(data): | |
| """要加很多功能,因为加入了大量的新块""" | |
| machine = ET.Element("Machine", version="1", bsgVersion="1.3", name="ai") | |
| # 创建 Global 元素 | |
| global_elem = ET.SubElement(machine, "Global") | |
| global_infos = data.pop(0) | |
| gp = global_infos["GlobalPosition"] | |
| gr = global_infos["GlobalRotation"] | |
| # if gp[1]<1.5: | |
| # print("警告,全局高度过低,小于1.5,调整到1.5") | |
| # gp[1]=1.5 | |
| position = ET.SubElement(global_elem, "Position", x=str(gp[0]), y=str(gp[1]), z=str(gp[2])) | |
| rotation = ET.SubElement(global_elem, "Rotation", x=str(gr[0]), y=str(gr[1]), z=str(gr[2]), w=str(gr[3])) | |
| # 创建 Data 元素 | |
| data_elem = ET.SubElement(machine, "Data") | |
| string_array = ET.SubElement(data_elem, "StringArray", key="requiredMods") | |
| # 创建 Blocks 元素 | |
| blocks_elem = ET.SubElement(machine, "Blocks") | |
| # 遍历 corners 数据并创建 Block 元素 | |
| for info in data: | |
| block_id = info['id'] | |
| if info['id']=='18_1': | |
| block_id ='18' | |
| block = ET.SubElement(blocks_elem, "Block", id=str(block_id), guid=info['guid']) | |
| transform = ET.SubElement(block, "Transform") | |
| info_p = info['Transform']['Position'] | |
| position = ET.SubElement(transform, "Position", x=str(info_p[0]), y=str(info_p[1]), z=str(info_p[2])) | |
| info_r = info['Transform']['Rotation'] | |
| rotation = ET.SubElement(transform, "Rotation", x=str(info_r[0]), y=str(info_r[1]), z=str(info_r[2]), w=str(info_r[3])) | |
| info_s = info['Transform']['Scale'] | |
| scale = ET.SubElement(transform, "Scale", x=str(info_s[0]), y=str(info_s[1]), z=str(info_s[2])) | |
| block_data = ET.SubElement(block, "Data") | |
| if str(info['id'])=="0": | |
| bmt = ET.SubElement(block_data, "Integer", key="bmt-version") | |
| bmt.text = "1" | |
| #线性块设置坐标 | |
| if str(info['id'])=="9": | |
| bmt = ET.SubElement(block_data,"Single",key = "bmt-slider") | |
| bmt.text = "10" | |
| bmt = ET.SubElement(block_data,"StringArray",key = "bmt-contract") | |
| bmt.text = "L" | |
| bmt = ET.SubElement(block_data,"Boolean",key = "bmt-toggle") | |
| bmt.text = "False" | |
| if str(info['id'])=="7" or str(info['id'])=="9": | |
| start_position = ET.SubElement(block_data,"Vector3",key = "start-position") | |
| ET.SubElement(start_position, "X").text = str(0) | |
| ET.SubElement(start_position, "Y").text = str(0) | |
| ET.SubElement(start_position, "Z").text = str(0) | |
| end_position = ET.SubElement(block_data,"Vector3",key = "end-position") | |
| ET.SubElement(end_position, "X").text = str(info['end-position'][0]) | |
| ET.SubElement(end_position, "Y").text = str(info['end-position'][1]) | |
| ET.SubElement(end_position, "Z").text = str(info['end-position'][2]) | |
| if str(info['id'])=="22": | |
| bmt = ET.SubElement(block_data, "Integer", key="bmt-version") | |
| bmt.text = "1" | |
| bmt = ET.SubElement(block_data,"Single",key = "bmt-speed") | |
| bmt.text = "1" | |
| bmt = ET.SubElement(block_data,"Single",key = "bmt-acceleration") | |
| bmt.text = "Infinity" | |
| bmt = ET.SubElement(block_data, "Boolean", key="bmt-auto-brake") | |
| bmt.text = "True" | |
| bmt = ET.SubElement(block_data, "Boolean", key="flipped") | |
| bmt.text = "False" | |
| if str(info['id'])=="35": | |
| bmt = ET.SubElement(block_data,"Single",key = "bmt-mass") | |
| bmt.text = "3" | |
| #轮子镜像处理 | |
| if "auto" in info: | |
| bmt = ET.SubElement(block_data, "Boolean", key="bmt-automatic") | |
| bmt.text = "True" | |
| bmt = ET.SubElement(block_data, "Boolean", key="bmt-auto-brake") | |
| bmt.text = "False" | |
| if "flip" in info and info["flip"]: | |
| bmt = ET.SubElement(block_data, "Boolean", key="flipped") | |
| bmt.text = "True" | |
| if "WheelDoubleSpeed" in info and info["WheelDoubleSpeed"]: | |
| bmt = ET.SubElement(block_data, "Single", key="bmt-speed") | |
| bmt.text = "2" | |
| # 将 ElementTree 转换为字符串 | |
| tree = ET.ElementTree(machine) | |
| ET.indent(tree, space="\t", level=0) | |
| xml_str = ET.tostring(machine, encoding="utf-8", method="xml", xml_declaration=True).decode("utf-8") | |
| return xml_str | |
| def json_to_xml(input_obj): | |
| if isinstance(input_obj,str): | |
| content = extract_json_from_string(input_obj) | |
| elif isinstance(input_obj,list): | |
| content = input_obj | |
| else: | |
| raise TypeError('Please make sure input type') | |
| block_details = content | |
| block_details = convert_to_numpy(block_details) | |
| xml_block_details,block_details,_ = llm2xml_filetree(block_details, | |
| BLOCKPROPERTYPATH, | |
| selected_menu=None) | |
| _,_,_,_,_,_,_,_ = llm_feedback_3d(block_sizes=BLOCKPROPERTYPATH, | |
| xml_block_details=xml_block_details, | |
| block_details = block_details) | |
| xml_string = create_xml(xml_block_details) | |
| return xml_string |