Skip to content

Latest commit

 

History

History
341 lines (228 loc) · 11.3 KB

arc.md

File metadata and controls

341 lines (228 loc) · 11.3 KB

为了学好Rust也是拼了系列-数学库-圆弧

定义

圆弧是指圆周上的一段弧线,即由圆的边界上的两个点确定的曲线部分。在数学中,我们用一些概念来描述和度量圆弧,主要包括弧长和弧度。

  1. 弧长(Arc Length): 弧长是指沿着圆周测量的实际长度。如果我们知道圆的半径($$r$$)和圆弧的中心角($$\theta$$,用弧度表示),可以通过以下公式计算弧长($$s$$):

    $$ s = r \cdot \theta $$

    弧长等于半径与中心角的乘积。这个公式表达了圆弧长度与半径和角度之间的关系。

  2. 弧度(Radian): 弧度是一种角度的度量单位,定义为半径长的弧所对应的角度。一个完整的圆弧对应的角度是 $$2\pi$$ 弧度,其中 $$\pi$$ 是圆周率,约为3.14159。如果一个圆弧的中心角为 $$\theta$$ 弧度,那么这个角所对应的弧长就是 $$r \cdot \theta$$

    使用弧度的优势在于它与圆的半径直接相关,便于利用三角函数等工具进行更复杂的数学运算。

  3. 角度的起始方向: 角度通常是从坐标轴的正方向开始测量的,按照逆时针方向为正。在标准的坐标系中,x 轴指向右侧,y 轴指向上方。因此,初始角度为零度,即在 x 轴正方向上。逆时针旋转角度被定义为正的,而顺时针旋转角度被定义为负的。

程序定义如下

#[derive(Debug, PartialEq)]
struct Arc {
    radius: f64,
    theta: f64, // 中心角,以弧度表示
}

求解圆弧

以弧长和半径求圆弧

在数学上,已知圆弧的弧长 $$s$$ 和半径 $$r$$,可以通过解方程 $$s = r \cdot \theta$$ 来求解圆弧的中心角 $$\theta$$

计算圆弧中心角的步骤如下:

  1. 建立方程: 根据圆弧的性质,弧长 $$s$$ 和半径 $$r$$ 满足关系式 $$s = r \cdot \theta$$

  2. 设定初始猜测值: 选择一个初始猜测值 $$\theta_0$$。通常可以选择 $$\theta_0 = \frac{s}{r}$$

  3. 迭代过程: 使用牛顿迭代公式进行迭代,直到收敛到方程的解: $$ \theta_{n+1} = \theta_n - \frac{f(\theta_n)}{f'(\theta_n)} $$ 其中,$$f(\theta) = r \cdot \theta - s$$ 是方程左侧的函数,$$f'(\theta)$$ 是其导数。

  4. 迭代终止条件: 迭代过程终止的条件通常是当 $$\theta_{n+1}$$$$\theta_n$$ 之间的差异小于设定的精度要求时,即 $$\left|\theta_{n+1} - \theta_n\right| < \epsilon$$,其中 $$\epsilon$$ 是预设的精度。

  5. 得到解: 当迭代终止时,得到的 $$\theta$$ 值即为圆弧的中心角。

程序解如下

pub fn from_arc_length_and_radius(arc_length: f64, radius: f64) -> Arc {
    // 初始猜测中心角的值
    let mut theta_guess = arc_length / radius;

    // 牛顿迭代法求解方程 s = r * theta
    let epsilon = 1e-10; // 精度要求
    let mut theta_prev;

    loop {
        theta_prev = theta_guess;
        let f_theta = radius * theta_prev - arc_length;
        let f_prime_theta = radius; // 导数 f'(θ) = r
        theta_guess = theta_prev - f_theta / f_prime_theta;

        if (theta_guess - theta_prev).abs() < epsilon {
            break;
        }
    }

    Arc {
        radius,
        theta: theta_guess,
    }
}

已知弦长和半径求圆弧

在数学上,如果已知一个圆弧的弦长 $$l$$ 和半径 $$r$$,我们可以通过以下步骤计算该圆弧的中心角:

  1. 弦长和半径的关系: 弦长 $$l$$ 和半径 $$r$$ 之间的关系由以下三角函数关系给出: $$ l = 2r \sin\left(\frac{\theta}{2}\right) $$

  2. 计算中心角: 通过解方程得到中心角 $$\theta$$ 的表达式: $$ \theta = 2 \arcsin\left(\frac{l}{2r}\right) $$ 将给定的弦长 $$l$$ 和半径 $$r$$ 代入该表达式,计算得到中心角 $$\theta$$ 的值。

程序解如下

/// 已知弦长和半径求圆弧
pub fn from_chord_length_and_radius(chord_length: f64, radius: f64) -> Arc {
    // 计算中心角的值
    let theta = 2.0 * f64::asin(chord_length / (2.0 * radius));

    Arc {
        radius,
        theta,
    }
}

已知圆弧的两个端点坐标求圆弧

已知圆弧的两个端点坐标 $$(x_1, y_1)$$$$(x_2, y_2)$$,我们可以通过以下步骤求解圆弧的中心角:

  1. 计算半径: 使用两个端点的坐标计算圆心到端点的距离,即半径 $$r$$。 $$ r = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2} $$

  2. 计算中心坐标: 圆心的坐标可以通过两个端点的中点得到,即 $$(x_c, y_c) = \left(\frac{x_1 + x_2}{2}, \frac{y_1 + y_2}{2}\right)$$

  3. 计算中心角: 中心角 $$\theta$$ 可以通过两个向量的夹角计算。设向量 $$\vec{v}_1$$$$\vec{v}_2$$ 分别为圆心指向两个端点的向量,使用向量的点乘和反余弦函数计算夹角。 $$ \cos(\theta) = \frac{\vec{v}_1 \cdot \vec{v}_2}{|\vec{v}_1| |\vec{v}_2|} $$ $$ \theta = \arccos\left(\frac{\vec{v}_1 \cdot \vec{v}_2}{|\vec{v}_1| |\vec{v}_2|}\right) $$

程序解如下

/// 已知圆弧的两个端点坐标求圆弧
pub fn from_endpoints(x1: f64, y1: f64, x2: f64, y2: f64) -> Arc {
    // 计算半径
    let radius = ((x2 - x1).powi(2) + (y2 - y1).powi(2)).sqrt();

    // 计算圆心坐标
    let center_x = (x1 + x2) / 2.0;
    let center_y = (y1 + y2) / 2.0;

    // 计算中心角
    let v1_x = x1 - center_x;
    let v1_y = y1 - center_y;
    let v2_x = x2 - center_x;
    let v2_y = y2 - center_y;

    let dot_product = v1_x * v2_x + v1_y * v2_y;
    let magnitude_product = (v1_x.powi(2) + v1_y.powi(2)).sqrt() * (v2_x.powi(2) + v2_y.powi(2)).sqrt();

    let cos_theta = dot_product / magnitude_product;
    let theta = cos_theta.acos();

    Arc { radius, theta }
}

已知圆弧的面积和半径求圆弧

已知圆弧的面积 $$A$$ 和半径 $$r$$,我们可以通过以下步骤计算圆弧的中心角:

  1. 计算弧长: 弧长 $$s$$ 与圆弧的面积 $$A$$ 之间有以下关系: $$ s = r \cdot \theta $$ 弧长与半径和中心角之间的关系可以通过弧长的定义推导而来。

  2. 计算中心角: 中心角 $$\theta$$ 与弧长 $$s$$ 之间的关系为: $$ \theta = \frac{s}{r} $$ 将计算得到的弧长 $$s$$ 和给定的半径 $$r$$ 代入上述公式,即可计算中心角 $$\theta$$ 的值。

程序解如下

/// 已知圆弧的面积和半径求圆弧
pub fn from_area_and_radius(area: f64, radius: f64) -> Arc {
    // 计算弧长
    let arc_length = (area * 2.0 / radius).sqrt();

    // 计算中心角
    let theta = arc_length / radius;

    Arc { radius, theta }
}

圆弧上的点

要均匀返回圆弧上指定数量的点,可以利用参数方程来实现。假设我们要在圆弧上均匀生成 $$n$$ 个点,可以使用如下的参数方程:

$$ x_i = x_c + r \cos\left(\frac{2\pi i}{n}\right) $$ $$ y_i = y_c + r \sin\left(\frac{2\pi i}{n}\right) $$

其中:

  • $$(x_c, y_c)$$ 是圆心坐标。
  • $$r$$ 是半径。
  • $$i$$ 是点的索引,取值范围为 $$0 \leq i &lt; n$$

这个方程保证了 $$n$$ 个点在圆弧上均匀分布,覆盖整个圆弧。在计算中心角时使用了 $$2\pi$$ 是为了确保角度在整个圆周上平均分布。

程序解如下

/// 生成圆弧上的点
pub fn generate_points(&self, num_points: usize) -> Vec<Point> {
    let mut points = Vec::with_capacity(num_points);

    for i in 0..num_points {
        let theta_increment = self.theta / (num_points as f64 - 1.0);
        let current_theta = i as f64 * theta_increment;
        let x = self.radius * current_theta.cos();
        let y = self.radius * current_theta.sin();
        points.push(Point { x, y });
    }

    points
}

切线方程

求解圆弧的切线通常需要考虑切线的斜率和过切点的圆的切线方程。

  1. 计算切线的斜率: 圆弧上的切线斜率等于圆弧在该点的导数。对于极坐标方程 $$x = r \cos(\theta)$$$$y = r \sin(\theta)$$,求导并计算导数在特定角度 $$\theta$$ 处的值。

    如果你知道切线经过的圆弧上的点,可以使用该点的坐标计算切线斜率。对于点 $$(x, y)$$,切线的斜率等于 $$\frac{dy}{dx}$$

  2. 切线方程: 切线方程可以使用点斜式或一般式表示。使用点斜式时,切线方程为: $$ y - y_1 = m(x - x_1) $$ 其中,$$(x_1, y_1)$$ 是切线上的已知点,$$m$$ 是切线的斜率。

    使用一般式时,切线方程为: $$ Ax + By = C $$ 这里的 $$A$$、$$B$$ 和 $$C$$ 是与切线方向和位置相关的常数。

  3. 过切点的圆的切线方程: 圆弧上的切线也是过圆心的半径。因此,切线方程还可以通过圆的半径和切点的坐标得到。设切点坐标为 $$(x_0, y_0)$$,圆心坐标为 $$(h, k)$$,圆的半径为 $$r$$,切线方程为: $$ (x - h)(x_0 - h) + (y - k)(y_0 - k) = r^2 $$

程序解如下

#[derive(Debug)]
pub struct Tangent {
    pub A: f64,
    pub B: f64,
    pub C: f64,
}

#[derive(Debug, PartialEq)]
struct Arc {
    radius: f64,
    theta: f64, // 中心角,以弧度表示
}

impl Arc {
    // 计算圆弧上一点的坐标
    pub fn point_on_arc(&self, angle: f64) -> (f64, f64) {
        let x = self.radius * angle.cos();
        let y = self.radius * angle.sin();
        (x, y)
    }

    // 计算圆弧上的切线方程
    fn tangent_at_point(&self, angle: f64) -> Tangent {
        // 计算两个相邻点的坐标
        let (x1, y1) = self.point_on_arc(angle);
        let epsilon = 1e-8;
        let (x2, y2) = self.point_on_arc(angle + epsilon);

        // 计算切线方程的一般式表示
        let a = y2 - y1;
        let b = x1 - x2;
        let c = x2 * y1 - x1 * y2;

        Tangent { A: a, B: b, C: c }
    }
}

法线方程

法线是与切线垂直的线。在数学上,法线方程表示的是与曲线(比如圆弧)的切线垂直的直线。法线方程通常可以通过切线方程的斜率来求解。

对于给定的切线方程 $$\text{Tangent}$$(用一般式表示为 $$Ax + By = C$$),法线的斜率可以通过切线的斜率的负倒数得到。这是因为两条垂直线的斜率乘积为 -1。

法线的一般式表示为 $$Ax + By = C'$$,其中 $$A$$、$$B$$ 和 $$C'$$ 是法线的常数,可以通过给定点的坐标来确定。

具体的步骤如下:

  1. 计算切线的斜率 $$m$$ 切线方程 $$\text{Tangent}$$ 的斜率 $$m$$$$-\frac{A}{B}$$

  2. 计算法线的斜率 $$m'$$ 法线的斜率 $$m'$$ 是切线斜率 $$m$$ 的负倒数,即 $$m' = \frac{B}{A}$$

  3. 通过给定点计算法线方程的常数 $$C'$$ 使用法线的斜率 $$m'$$ 和给定点的坐标 $$(x_0, y_0)$$,可以得到法线方程的一般式: $$ Ax + By = C' $$

程序解如下

// 计算法线方程
pub fn normal_at_point(&self, angle: f64) -> LinearEquation {
    // 计算切线方程的斜率
    let tangent_slope = -(self.radius * self.theta.sin()) / (self.radius * self.theta.cos());

    // 计算法线斜率
    let normal_slope = -1.0 / tangent_slope;

    // 计算法线方程的常数
    let (x0, y0) = self.point_on_arc(angle);
    let normal_constant = y0 - normal_slope * x0;

    LinearEquation {
        A: normal_slope,
        B: -1.0,
        C: normal_constant,
    }
}