You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

已知抛射角,在两点间绘制抛物弧的C#实现技术问询

Hey there! Let's tackle this problem step by step—since we're ignoring all physics like gravity and external forces, this boils down to finding a circular arc that starts at point A, follows the given launch direction, and ends at point B. No fancy physics equations needed, just good old geometry!

数学原理推导

First, let's list out our knowns clearly:

  • Point A: (Ax, Ay)
  • Point B: (Bx, By)
  • Launch angle θ (measured counterclockwise from the positive x-axis; adjust if your coordinate system uses a different convention)

Core Idea

Without any external forces, the projectile moves in a circular path (since its velocity vector stays perpendicular to the radius of the circle at all points). Our goal is to find the circle's center, radius, and the specific arc connecting A to B.

Step 1: Find the Normal Line at Point A

The launch direction gives us the tangent vector to the arc at A: v = (cosθ, sinθ). The circle's center lies along the line perpendicular to this tangent (the normal line) passing through A.

  • The slope of the normal line is -cosθ/sinθ (negative reciprocal of the tangent's slope tanθ).
  • Equation of the normal line: (y - Ay) = (-cosθ/sinθ)(x - Ax) (handle vertical/horizontal tangents as special cases later).

Step 2: Find the Perpendicular Bisector of AB

The center of the circle must also lie on the perpendicular bisector of segment AB (since all points on the bisector are equidistant from A and B).

  • Midpoint of AB: M = ((Ax+Bx)/2, (Ay+By)/2)
  • Slope of AB: m_ab = (By - Ay)/(Bx - Ax)
  • Slope of the perpendicular bisector: -1/m_ab
  • Equation of the bisector: (y - My) = (- (Bx - Ax)/(By - Ay))(x - Mx)

Step 3: Calculate the Circle's Center

The center O = (Ox, Oy) is the intersection of the normal line (from Step 1) and the perpendicular bisector (from Step 2). Solve the two linear equations to get Ox and Oy.

Step 4: Determine Arc Parameters

  • Radius r: Distance from O to A (or O to B, they're equal): r = √[(Ox - Ax)² + (Oy - Ay)²]
  • Start angle: Angle of point A relative to the center (use atan2(Ay - Oy, Ax - Ox) for radians)
  • Sweep angle: Difference between the angle of B and A relative to the center. We need to ensure this angle follows the launch direction (not the opposite arc).
C# Implementation

Below is a complete implementation that calculates the arc parameters and draws it (using System.Drawing for WinForms, but you can adapt the math to other frameworks like Unity).

Helper Structures & Parameter Calculation

using System;
using System.Drawing;

public struct PointF
{
    public float X;
    public float Y;
    public PointF(float x, float y)
    {
        X = x;
        Y = y;
    }
}

public static class ArcCalculator
{
    public static bool CalculateArcParameters(PointF A, PointF B, float launchAngleRadians, out PointF center, out float radius, out float startAngleDeg, out float sweepAngleDeg)
    {
        // Initialize default values
        center = new PointF();
        radius = 0f;
        startAngleDeg = 0f;
        sweepAngleDeg = 0f;

        float cosTheta = (float)Math.Cos(launchAngleRadians);
        float sinTheta = (float)Math.Sin(launchAngleRadians);
        float midX = (A.X + B.X) / 2f;
        float midY = (A.Y + B.Y) / 2f;

        // Case 1: Tangent is horizontal (theta = 0 or π radians)
        if (Math.Abs(sinTheta) < 1e-6)
        {
            if (Math.Abs(A.X - B.X) < 1e-6)
            {
                // AB is vertical; center is midpoint's y, same x as A
                center = new PointF(A.X, midY);
            }
            else
            {
                // Calculate y on AB's perpendicular bisector at x = Ax
                float slopePerp = (A.X - B.X) / (B.Y - A.Y);
                float y = slopePerp * (A.X - midX) + midY;
                center = new PointF(A.X, y);
            }
        }
        // Case 2: Tangent is vertical (theta = π/2 or 3π/2 radians)
        else if (Math.Abs(cosTheta) < 1e-6)
        {
            // Calculate x on AB's perpendicular bisector at y = Ay
            float slopePerp = (B.Y - A.Y) / (A.X - B.X);
            float x = (A.Y - midY) / slopePerp + midX;
            center = new PointF(x, A.Y);
        }
        // General case: Solve intersection of normal line and perpendicular bisector
        else
        {
            float m1 = -cosTheta / sinTheta; // Slope of normal line at A
            float b1 = A.Y - m1 * A.X;       // Y-intercept of normal line

            float m2 = -(B.X - A.X) / (B.Y - A.Y); // Slope of AB's perpendicular bisector
            float b2 = midY - m2 * midX;           // Y-intercept of bisector

            // Check if lines are parallel (no solution)
            if (Math.Abs(m1 - m2) < 1e-6)
            {
                return false;
            }

            // Solve for intersection point
            float x = (b2 - b1) / (m1 - m2);
            float y = m1 * x + b1;
            center = new PointF(x, y);
        }

        // Calculate radius
        radius = (float)Math.Sqrt(Math.Pow(center.X - A.X, 2) + Math.Pow(center.Y - A.Y, 2));

        // Convert radians to degrees (System.Drawing uses degrees, clockwise from x-axis)
        float startAngleRad = (float)Math.Atan2(A.Y - center.Y, A.X - center.X);
        startAngleDeg = startAngleRad * 180f / (float)Math.PI;

        float endAngleRad = (float)Math.Atan2(B.Y - center.Y, B.X - center.X);
        float endAngleDeg = endAngleRad * 180f / (float)Math.PI;

        // Determine correct sweep direction (matches launch direction)
        float desiredNormalDir = launchAngleRadians + (float)Math.PI / 2f;
        float centerToARad = (float)Math.Atan2(A.Y - center.Y, A.X - center.X);

        if (Math.Abs(Math.Abs(desiredNormalDir - centerToARad) % (2 * Math.PI)) > 1e-6)
        {
            sweepAngleDeg = startAngleDeg - endAngleDeg;
        }
        else
        {
            sweepAngleDeg = endAngleDeg - startAngleDeg;
        }

        // Ensure we use the smaller arc (avoid full circle sweeps)
        if (Math.Abs(sweepAngleDeg) > 180f)
        {
            sweepAngleDeg = sweepAngleDeg > 0 ? sweepAngleDeg - 360f : sweepAngleDeg + 360f;
        }

        return true;
    }
}

Example: Drawing the Arc in WinForms

private void Form1_Paint(object sender, PaintEventArgs e)
{
    // Define your points and launch angle
    PointF pointA = new PointF(50f, 200f);
    PointF pointB = new PointF(300f, 100f);
    float launchAngle = (float)(Math.PI / 3); // 60 degrees counterclockwise

    if (ArcCalculator.CalculateArcParameters(pointA, pointB, launchAngle, out PointF center, out float radius, out float startAngle, out float sweepAngle))
    {
        // Draw the arc
        using (Pen arcPen = new Pen(Color.Red, 2f))
        {
            RectangleF circleBounds = new RectangleF(center.X - radius, center.Y - radius, 2 * radius, 2 * radius);
            e.Graphics.DrawArc(arcPen, circleBounds, startAngle, sweepAngle);
        }

        // Draw points A and B
        using (Brush pointBrush = new SolidBrush(Color.Blue))
        {
            e.Graphics.FillEllipse(pointBrush, pointA.X - 5f, pointA.Y - 5f, 10f, 10f);
            e.Graphics.FillEllipse(pointBrush, pointB.X - 5f, pointB.Y - 5f, 10f, 10f);
        }
    }
    else
    {
        e.Graphics.DrawString("No valid arc found!", Font, Brushes.Black, 10f, 10f);
    }
}

Key Notes

  • The code handles edge cases (horizontal/vertical launch directions) to avoid division by zero.
  • We convert radians to degrees because System.Drawing.Graphics.DrawArc uses degree-based angles (clockwise from the positive x-axis).
  • The sweep angle is adjusted to ensure we draw the correct arc (not the opposite one) that matches the launch direction.

内容的提问来源于stack exchange,提问作者Sandoichi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:44:11