< Summary

Information
Class: CounterDrone.Core.Algorithms.Kinematics
Assembly: CounterDrone.Core
File(s): C:\Users\Tellme\apps\CounterDroneBackend\src\CounterDrone.Core\Algorithms\Kinematics.cs
Line coverage
73%
Covered lines: 47
Uncovered lines: 17
Coverable lines: 64
Total lines: 105
Line coverage: 73.4%
Branch coverage
57%
Covered branches: 12
Total branches: 21
Branch coverage: 57.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
WindToVector(...)33.33%13962.5%
CalculateLaunchAngle(...)50%44100%
ParabolicPosition(...)100%11100%
Distance2D(...)100%210%
Distance3D(...)100%210%
PointInPolygon(...)87.5%88100%

File(s)

C:\Users\Tellme\apps\CounterDroneBackend\src\CounterDrone.Core\Algorithms\Kinematics.cs

#LineLine coverage
 1using System;
 2using CounterDrone.Core.Models;
 3
 4namespace CounterDrone.Core.Algorithms
 5{
 6    /// <summary>通用运动学 / 几何工具</summary>
 7    public static class Kinematics
 8    {
 9        /// <summary>风向 → 单位速度矢量 (X, 0, Z),右手系 Y-up</summary>
 10        public static (float X, float Y, float Z) WindToVector(WindDirection dir, float speed)
 593011        {
 593012            var angle = dir switch
 593013            {
 464214                WindDirection.N => 0f,
 015                WindDirection.NE => 45f,
 13616                WindDirection.E => 90f,
 017                WindDirection.SE => 135f,
 018                WindDirection.S => 180f,
 019                WindDirection.SW => 225f,
 115220                WindDirection.W => 270f,
 021                WindDirection.NW => 315f,
 022                _ => 0f,
 593023            };
 593024            var rad = angle * (float)Math.PI / 180f;
 593025            return ((float)Math.Sin(rad) * speed, 0, (float)Math.Cos(rad) * speed);
 593026        }
 27
 28        /// <summary>计算抛物线炮弹的发射角</summary>
 29        /// <param name="range">水平距离 (m)</param>
 30        /// <param name="muzzleVelocity">初速 (m/s)</param>
 31        /// <param name="releaseAltitude">释放高度 (m),弹道顶点必须 ≥ 此值</param>
 32        /// <returns>发射角 (rad),取低弹道</returns>
 33        public static float CalculateLaunchAngle(float range, float muzzleVelocity, float releaseAltitude)
 2934        {
 2935            var v2 = muzzleVelocity * muzzleVelocity;
 2936            var g = 9.81f;
 37
 38            // 1. 射程所需角:θr = arcsin(R*g/v²) / 2
 2939            var rangeRatio = range * g / v2;
 2940            var angleRange = rangeRatio >= 1.0f
 2941                ? 45f * (float)Math.PI / 180f
 2942                : (float)Math.Asin(rangeRatio) / 2f;
 43
 44            // 2. 释放高度所需最小角:H = v²*sin²(θ)/(2g) → sin(θ) = √(2gH)/v
 2945            var sinMinHeight = (float)Math.Sqrt(2f * g * releaseAltitude * 1.05f) / muzzleVelocity;
 2946            var angleHeight = sinMinHeight >= 1.0f
 2947                ? 90f * (float)Math.PI / 180f
 2948                : (float)Math.Asin(sinMinHeight);
 49
 2950            return Math.Max(angleRange, angleHeight);
 2951        }
 52
 53        /// <summary>根据发射参数计算抛物线位置</summary>
 54        public static (float X, float Y, float Z) ParabolicPosition(
 55            float startX, float startY, float startZ,
 56            float launchAngle, float azimuth,
 57            float muzzleVelocity, float time)
 73858        {
 73859            var cosA = (float)Math.Cos(launchAngle);
 73860            var sinA = (float)Math.Sin(launchAngle);
 73861            var g = 9.81f;
 62
 73863            var horizontalDist = muzzleVelocity * cosA * time;
 73864            var x = startX + horizontalDist * (float)Math.Sin(azimuth);
 73865            var z = startZ + horizontalDist * (float)Math.Cos(azimuth);
 73866            var y = startY + muzzleVelocity * sinA * time - 0.5f * g * time * time;
 67
 73868            return (x, y, z);
 73869        }
 70
 71        /// <summary>点到矩形距离(简化判定)</summary>
 72        public static float Distance2D(float x1, float z1, float x2, float z2)
 073        {
 074            var dx = x2 - x1;
 075            var dz = z2 - z1;
 076            return (float)Math.Sqrt(dx * dx + dz * dz);
 077        }
 78
 79        /// <summary>点到三维点距离</summary>
 80        public static float Distance3D(float x1, float y1, float z1, float x2, float y2, float z2)
 081        {
 082            var dx = x2 - x1;
 083            var dy = y2 - y1;
 084            var dz = z2 - z1;
 085            return (float)Math.Sqrt(dx * dx + dy * dy + dz * dz);
 086        }
 87
 88        /// <summary>射线法 — 点是否在水平多边形内</summary>
 89        public static bool PointInPolygon(float px, float pz, ReadOnlySpan<(float X, float Z)> vertices)
 7790        {
 7791            if (vertices.Length < 3) return false;
 7792            bool inside = false;
 7793            int j = vertices.Length - 1;
 77094            for (int i = 0; i < vertices.Length; i++)
 30895            {
 61696                var xi = vertices[i].X; var zi = vertices[i].Z;
 61697                var xj = vertices[j].X; var zj = vertices[j].Z;
 30898                if ((zi > pz) != (zj > pz) && px < (xj - xi) * (pz - zi) / (zj - zi) + xi)
 14599                    inside = !inside;
 308100                j = i;
 308101            }
 77102            return inside;
 77103        }
 104    }
 105}