|
|
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()
|
|
|
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 = 0.005
|
|
|
|
|
|
|
|
|
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])
|
|
|
|
|
|
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])
|
|
|
|
|
|
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=[]
|
|
|
|
|
|
|
|
|
for i in range(len(my_building_points)):
|
|
|
|
|
|
|
|
|
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):
|
|
|
|
|
|
|
|
|
if np.isclose(v_from[0], 0) and np.isclose(v_from[1], 0):
|
|
|
axis = np.array([0, 1, 0])
|
|
|
else:
|
|
|
axis = np.cross(v_from, np.array([0, 1, 0]))
|
|
|
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()
|
|
|
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"])
|
|
|
|
|
|
|
|
|
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])
|
|
|
|
|
|
|
|
|
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))
|
|
|
|
|
|
|
|
|
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不存在")
|
|
|
|
|
|
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 = []
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
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]):
|
|
|
|
|
|
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]
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
if blocks_to_delete:
|
|
|
|
|
|
block_details = [b for b in block_details if int(b["order_id"]) not in blocks_to_delete]
|
|
|
|
|
|
|
|
|
processed_details = get_3d_from_llm(block_sizes, block_details, gp, gr_matrix, log=False)
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
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=""
|
|
|
|
|
|
|
|
|
for i in range(len(block_details)):
|
|
|
|
|
|
for j in range(i + 1, len(block_details)):
|
|
|
if "GlobalPosition" in block_details[i] or "GlobalPosition" in block_details[j]: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
|
|
|
|
|
|
|
|
|
results = check_overlap_or_connection(corners1, corners2)
|
|
|
if results=="connected":
|
|
|
|
|
|
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":
|
|
|
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":
|
|
|
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:
|
|
|
|
|
|
found_head = "共发现"
|
|
|
overlaps_head="处重叠"
|
|
|
overlaps_info+=f"{found_head} {len(overlaps)} {overlaps_head}\n"
|
|
|
else:
|
|
|
|
|
|
overlaps_info+="没有错误"
|
|
|
|
|
|
if vis:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
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"]
|
|
|
|
|
|
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}"
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
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="gpt")
|
|
|
|
|
|
|
|
|
global_elem = ET.SubElement(machine, "Global")
|
|
|
global_infos = data.pop(0)
|
|
|
gp = global_infos["GlobalPosition"]
|
|
|
gr = global_infos["GlobalRotation"]
|
|
|
|
|
|
|
|
|
|
|
|
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_elem = ET.SubElement(machine, "Data")
|
|
|
string_array = ET.SubElement(data_elem, "StringArray", key="requiredMods")
|
|
|
|
|
|
|
|
|
blocks_elem = ET.SubElement(machine, "Blocks")
|
|
|
|
|
|
|
|
|
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"
|
|
|
|
|
|
|
|
|
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 |