using System; using MultiWheelC.TrajectoryPlanning.Utils; namespace MultiWheelC.TrajectoryPlanning.CoarsePath.Vehicle; /// 以车辆几何中心为原点的扩大旋转矩形。 internal sealed class VehicleFootprint { private VehicleFootprint(Pose2D pose, double halfLengthMeters, double halfWidthMeters) { CenterX = pose.X; CenterY = pose.Y; HalfLengthMeters = halfLengthMeters; HalfWidthMeters = halfWidthMeters; AxisLongitudinalX = Math.Cos(pose.Heading); AxisLongitudinalY = Math.Sin(pose.Heading); AxisLateralX = -AxisLongitudinalY; AxisLateralY = AxisLongitudinalX; CircumscribedRadiusMeters = Math.Sqrt(halfLengthMeters * halfLengthMeters + halfWidthMeters * halfWidthMeters); double minX = double.PositiveInfinity; double maxX = double.NegativeInfinity; double minY = double.PositiveInfinity; double maxY = double.NegativeInfinity; for (int index = 0; index < 4; index++) { GetCorner(index, out double x, out double y); minX = Math.Min(minX, x); maxX = Math.Max(maxX, x); minY = Math.Min(minY, y); maxY = Math.Max(maxY, y); } MinX = minX; MaxX = maxX; MinY = minY; MaxY = maxY; } public double CenterX { get; } public double CenterY { get; } public double HalfLengthMeters { get; } public double HalfWidthMeters { get; } public double AxisLongitudinalX { get; } public double AxisLongitudinalY { get; } public double AxisLateralX { get; } public double AxisLateralY { get; } public double CircumscribedRadiusMeters { get; } public double MinX { get; } public double MaxX { get; } public double MinY { get; } public double MaxY { get; } /// 创建包含车辆安全余量和临时扫掠余量的矩形。 public static bool TryCreate(Pose2D pose, VehicleParameters vehicle, double additionalMarginMeters, out VehicleFootprint footprint) { footprint = null; if (pose == null || vehicle == null || !NumericGuard.IsFinite(pose.X) || !NumericGuard.IsFinite(pose.Y) || !NumericGuard.IsFinite(pose.Heading) || !NumericGuard.IsPositiveFinite(vehicle.LengthMeters) || !NumericGuard.IsPositiveFinite(vehicle.WidthMeters) || !NumericGuard.IsFinite(vehicle.SafetyMarginMeters) || vehicle.SafetyMarginMeters < 0d || !NumericGuard.IsFinite(additionalMarginMeters) || additionalMarginMeters < 0d) return false; double totalMarginMeters = vehicle.SafetyMarginMeters + additionalMarginMeters; if (!NumericGuard.IsFinite(totalMarginMeters)) return false; double halfLengthMeters = vehicle.LengthMeters / 2d + totalMarginMeters; double halfWidthMeters = vehicle.WidthMeters / 2d + totalMarginMeters; if (!NumericGuard.IsPositiveFinite(halfLengthMeters) || !NumericGuard.IsPositiveFinite(halfWidthMeters)) return false; footprint = new VehicleFootprint(pose, halfLengthMeters, halfWidthMeters); return NumericGuard.IsFinite(footprint.CircumscribedRadiusMeters) && NumericGuard.IsFinite(footprint.MinX) && NumericGuard.IsFinite(footprint.MaxX) && NumericGuard.IsFinite(footprint.MinY) && NumericGuard.IsFinite(footprint.MaxY); } /// 获取指定角点。索引按逆时针顺序为 0 到 3。 public void GetCorner(int index, out double x, out double y) { double longitudinalSign; double lateralSign; switch (index) { case 0: longitudinalSign = 1d; lateralSign = 1d; break; case 1: longitudinalSign = -1d; lateralSign = 1d; break; case 2: longitudinalSign = -1d; lateralSign = -1d; break; case 3: longitudinalSign = 1d; lateralSign = -1d; break; default: throw new ArgumentOutOfRangeException(nameof(index)); } x = CenterX + longitudinalSign * HalfLengthMeters * AxisLongitudinalX + lateralSign * HalfWidthMeters * AxisLateralX; y = CenterY + longitudinalSign * HalfLengthMeters * AxisLongitudinalY + lateralSign * HalfWidthMeters * AxisLateralY; } }