import MilCon import typing import math class _HPos: x: str y: str z: str class RelativePos: def __init__(self, x: str | int | float = "~", y: str | int | float = "~", z: str | int | float = "~"): self.x: str = str(x) if isinstance(x, (int, float)) else x self.y: str = str(y) if isinstance(y, (int, float)) else y self.z: str = str(z) if isinstance(z, (int, float)) else z def __str__(self): return f"{self.x} {self.y} {self.z}" @classmethod def FromString(cls, s: str): parts = s.split() if len(parts) != 3 or not all(p.startswith('~') for p in parts): raise ValueError("Catch A Error As RelativePos ~x ~y ~z") return cls(*parts) def ToAbsolute(self, reference: _HPos=None): def ParseCoord(coord: str, ref): if coord.startswith('~'): return ref + (float(coord[1:]) if len(coord) > 1 else 0.0) else: return float(coord) ref_x = reference.x if reference and reference.x.startswith('~') else 0.0 ref_y = reference.y if reference and reference.y.startswith('~') else 0.0 ref_z = reference.z if reference and reference.z.startswith('~') else 0.0 abs_x = ParseCoord(self.x, ref_x) abs_y = ParseCoord(self.y, ref_y) abs_z = ParseCoord(self.z, ref_z) return RelativePos(abs_x, abs_y, abs_z) class LocalCoordinatesPos: def __init__(self, x: str | int | float = "^", y: str | int | float = "^", z: str | int | float = "^"): self.x: str = str(x) if isinstance(x, (int, float)) else x self.y: str = str(y) if isinstance(y, (int, float)) else y self.z: str = str(z) if isinstance(z, (int, float)) else z def __str__(self): return f"{self.x} {self.y} {self.z}" @classmethod def FromString(cls, s: str): parts = s.split() if len(parts) != 3 or not all(p.startswith('^') for p in parts): raise ValueError("Catch A Error As LocalCoordinatesPos ^x ^y ^z") return cls(*parts) def ToAbsolute(self, reference: _HPos=None): def ParseCoord(coord: str, ref): if coord.startswith('^'): return ref + (float(coord[1:]) if len(coord) > 1 else 0.0) else: return float(coord) ref_x = reference.x if reference and reference.x.startswith('^') else 0.0 ref_y = reference.y if reference and reference.y.startswith('^') else 0.0 ref_z = reference.z if reference and reference.z.startswith('^') else 0.0 abs_x = ParseCoord(self.x, ref_x) abs_y = ParseCoord(self.y, ref_y) abs_z = ParseCoord(self.z, ref_z) return LocalCoordinatesPos(abs_x, abs_y, abs_z) class Delta: def __init__(self, x: int = 0, y: int = 0, z: int = 0): self.x, self.y, self.z = x, y, z def __str__(self): return f"{self.x} {self.y} {self.z}" class Command(): def __init__(self, command): self.command = command def __str__(self): return self.command class ExecuteSubCommandRule(typing.TypedDict): mode: MilCon._execute_sub_commands text: Command def _Bool2Str(value: bool): return "true" if value else "false" def _WithValue(value: str | int): return f" {value}" if value else "" def _Pos1ToPos2(pos1: RelativePos, pos2: RelativePos, radios: int): abs_pos1 = pos1.ToAbsolute() abs_pos2 = pos2.ToAbsolute() x1, y1, z1 = float(abs_pos1.x), float(abs_pos1.y), float(abs_pos1.z) x2, y2, z2 = float(abs_pos2.x), float(abs_pos2.y), float(abs_pos2.z) delta_x = (x2 - x1) / (radios + 1) delta_y = (y2 - y1) / (radios + 1) delta_z = (z2 - z1) / (radios + 1) points = [] for i in range(1, radios + 1): x = x1 + delta_x * i y = y1 + delta_y * i z = z1 + delta_z * i points.append(RelativePos(x, y, z)) return points def _Pos1ToPos2WithPoints(radios: int, radian: int, pos1: RelativePos, pos2: RelativePos, *poses: RelativePos): # 参数验证 if radios <= len(poses) + 2: raise ValueError("radios must be greater than len(poses) + 2") if radian < 0: raise ValueError("radian must be non-negative") # 转换为绝对坐标 abs_pos1 = pos1.ToAbsolute() abs_poses = [p.ToAbsolute() for p in poses] abs_pos2 = pos2.ToAbsolute() # 所有点按顺序排列 all_points = [abs_pos1] + abs_poses + [abs_pos2] num_segments = len(all_points) - 1 # 计算每段的点数 points_per_segment = (radios - num_segments + 1) // num_segments remainder = (radios - num_segments + 1) % num_segments # 生成点 points = [] for i in range(num_segments): start = all_points[i] end = all_points[i + 1] # 当前段的点数 current_points = points_per_segment + (1 if i < remainder else 0) # 计算增量 delta_x = (float(end.x) - float(start.x)) / (current_points + 1) delta_y = (float(end.y) - float(start.y)) / (current_points + 1) delta_z = (float(end.z) - float(start.z)) / (current_points + 1) # 生成当前段的点 for j in range(1, current_points + 1): x = float(start.x) + delta_x * j y = float(start.y) + delta_y * j z = float(start.z) + delta_z * j # 如果 radian 不为 0 且 poses 存在,则在中间点附近向外偏移 if radian != 0 and len(poses) > 0 and i < len(poses): # 计算当前点与中间点的距离比例 (0-1) t = j / (current_points + 1) # 使用正弦函数生成弧形偏移 (0-π) offset = radian * math.sin(t * math.pi) # 计算3D垂直方向 # 获取前一个点和后一个点 prev_point = all_points[i] next_point = all_points[i+1] # 计算线段方向向量 direction = ( float(next_point.x) - float(prev_point.x), float(next_point.y) - float(prev_point.y), float(next_point.z) - float(prev_point.z) ) # 计算垂直方向 (使用简单的垂直向量) # 这里我们选择一个与方向向量垂直的向量 if direction[0] != 0 or direction[1] != 0: # 如果方向向量不是纯z轴方向,我们可以用叉积计算垂直向量 perpendicular = ( -direction[1], direction[0], 0 ) else: # 如果是纯z轴方向,用x轴作为垂直方向 perpendicular = (1, 0, 0) # 归一化垂直向量 length = math.sqrt(sum(p*p for p in perpendicular)) if length > 0: perpendicular = ( perpendicular[0]/length, perpendicular[1]/length, perpendicular[2]/length ) # 应用偏移 x += offset * perpendicular[0] y += offset * perpendicular[1] z += offset * perpendicular[2] points.append(RelativePos(x, y, z)) return points def _Pos1ToPos2DrawPi(pos1: RelativePos, pos2: RelativePos, points: int) -> typing.List[RelativePos]: """ Generate a list of points forming a circle with pos1 as the center, pos2 as a point on the circumference, and return num_points including pos1 at start and end. Args: pos1: Center of the circle (absolute coordinates) pos2: Point on the circumference (absolute coordinates) num_points: Number of points to generate (including start/end point) Returns: List of RelativePos points forming the circle, starting and ending with pos1 """ if points < 2: raise ValueError("Number of points must be at least 2 (including start/end)") # Convert positions to absolute float values center_x = float(pos1.x) center_y = float(pos1.y) center_z = float(pos1.z) point_x = float(pos2.x) point_y = float(pos2.y) point_z = float(pos2.z) # Calculate radius as distance from pos1 to pos2 dx = point_x - center_x dy = point_y - center_y dz = point_z - center_z radius = (dx**2 + dy**2 + dz**2) ** 0.5 # Determine the plane of the circle # Assume the circle is in the x-z plane if dy is zero, otherwise use x-y plane # This is a simplification and may need adjustment based on actual requirements points = [] for i in range(points): angle = 2 * math.pi * i / (points - 1) # Simplified 2D circle in x-z plane (y remains constant) x = center_x + radius * math.cos(angle) z = center_z + radius * math.sin(angle) y = center_y # y coordinate remains the same as center points.append(RelativePos(x, y, z)) return points '''def _Pos1ToPos2WithPoints(radios: int, pos1: RelativePos, pos2: RelativePos, *poses: RelativePos): if radios <= len(poses) + 2: raise ValueError("radios must be greater than len(poses) + 2") abs_pos1 = pos1.ToAbsolute() abs_poses = [p.ToAbsolute() for p in poses] abs_pos2 = pos2.ToAbsolute() all_points = [abs_pos1] + abs_poses + [abs_pos2] num_segments = len(all_points) - 1 points_per_segment = (radios - num_segments + 1) // num_segments remainder = (radios - num_segments + 1) % num_segments points = [] for i in range(num_segments): start = all_points[i] end = all_points[i + 1] current_points = points_per_segment + (1 if i < remainder else 0) delta_x = (float(end.x) - float(start.x)) / (current_points + 1) delta_y = (float(end.y) - float(start.y)) / (current_points + 1) delta_z = (float(end.z) - float(start.z)) / (current_points + 1) for j in range(1, current_points + 1): x = float(start.x) + delta_x * j y = float(start.y) + delta_y * j z = float(start.z) + delta_z * j points.append(RelativePos(x, y, z)) return points'''