在计算几何领域,半平面交算法是一个非常重要的基础算法。它广泛应用于多边形裁剪、可见性区域计算、线性规划等问题中。本教程将使用 Rust 语言,手把手带你从零实现一个清晰、高效且易于理解的半平面交算法,即使你是编程新手也能轻松上手。
在二维平面上,一条直线将平面分成两个部分,每一部分称为一个半平面(Half-plane)。通常我们用不等式表示,例如:\[ ax + by + c \leq 0 \]就定义了一个半平面。
当我们有多个这样的半平面时,它们的交集(即同时满足所有不等式的点的集合)可能是一个凸多边形、一条线段、一个点,或者为空。我们的目标就是求出这个交集区域。

半平面交的经典解法是使用双端队列(deque)维护当前交集的边界。基本步骤如下:
我们将使用 Rust 的标准库,配合一些简单的数学结构来实现。首先定义点和直线(半平面)的数据结构。
#[derive(Debug, Clone, Copy)]struct Point { x: f64, y: f64,}impl Point { fn new(x: f64, y: f64) -> Self { Point { x, y } } // 向量减法 fn sub(&self, other: &Point) -> Point { Point::new(self.x - other.x, self.y - other.y) } // 叉积:用于判断转向 fn cross(&self, other: &Point) -> f64 { self.x * other.y - self.y * other.x }}// 半平面由直线 ax + by + c <= 0 表示#[derive(Debug, Clone)]struct HalfPlane { a: f64, b: f64, c: f64,}impl HalfPlane { fn new(a: f64, b: f64, c: f64) -> Self { HalfPlane { a, b, c } } // 方向向量 (dx, dy) = (-b, a) fn direction(&self) -> Point { Point::new(-self.b, self.a) } // 判断点 p 是否在半平面内 fn contains(&self, p: &Point) -> bool { self.a * p.x + self.b * p.y + self.c <= 1e-9 }}fn intersect(h2: &HalfPlane, h2: &HalfPlane) -> Option<Point> { let det = h2.a * h2.b - h2.a * h2.b; if det.abs() < 1e-9 { return None; // 平行 } let x = (h2.b * h2.c - h2.b * h2.c) / det; let y = (h2.a * h2.c - h2.a * h2.c) / det; Some(Point::new(-x, -y))}我们需要根据方向向量的极角对半平面排序,注意处理方向相反的情况(保留更“紧”的约束)。
use std::cmp::Ordering;fn angle_cmp(h2: &HalfPlane, h2: &HalfPlane) -> Ordering { let d1 = h2.direction(); let d2 = h2.direction(); // 计算 atan2 角度 let ang1 = d1.y.atan2(d1.x); let ang2 = d2.y.atan2(d2.x); // 处理角度环绕问题 if (ang1 - ang2).abs() < 1e-9 { // 同方向:保留常数项更小的(约束更强) if h2.c < h2.c { Ordering::Less } else { Ordering::Greater } } else if ang1 < ang2 { Ordering::Less } else { Ordering::Greater }}use std::collections::VecDeque;fn half_plane_intersection(mut planes: Vec<HalfPlane>) -> Option<Vec<Point>> { // 1. 排序 planes.sort_by(|a, b| angle_cmp(a, b)); // 2. 去重(同方向只保留最强约束) let mut unique_planes = vec![]; for p in planes { if unique_planes.is_empty() || angle_cmp(&unique_planes.last().unwrap(), &p) != Ordering::Equal { unique_planes.push(p); } } let n = unique_planes.len(); if n == 0 { return Some(vec![]); } // 3. 双端队列 let mut dq: VecDeque<HalfPlane> = VecDeque::new(); let mut points: VecDeque<Point> = VecDeque::new(); for i in 0..n { let cur = &unique_planes[i]; // 检查队尾 while dq.len() >= 2 { let last = dq.pop_back().unwrap(); let second_last = dq.back().unwrap().clone(); if let Some(inter) = intersect(&second_last, &last) { if cur.contains(&inter) { dq.push_back(last); break; } points.pop_back(); } else { dq.push_back(last); break; } } dq.push_back(cur.clone()); // 记录新交点 if dq.len() >= 2 { if let Some(p) = intersect(dq.get(dq.len()-2).unwrap(), dq.back().unwrap()) { points.push_back(p); } } // 检查队首 while dq.len() >= 2 { let front = dq.pop_front().unwrap(); let second_front = dq.front().unwrap().clone(); if let Some(inter) = intersect(&front, &second_front) { let last_plane = dq.back().unwrap(); if last_plane.contains(&inter) { dq.push_front(front); break; } points.pop_front(); } else { dq.push_front(front); break; } } } // 4. 清理首尾冲突 while dq.len() >= 2 { let last = dq.pop_back().unwrap(); if let Some(inter) = intersect(dq.front().unwrap(), &last) { if !dq.back().unwrap().contains(&inter) { points.pop_back(); } else { dq.push_back(last); break; } } else { dq.push_back(last); break; } } if points.is_empty() { None } else { Some(points.into()) }}通过以上步骤,我们成功用 Rust 实现了 半平面交算法。该算法的时间复杂度为 \(O(n \log n)\),主要开销在排序阶段。
这项技术在以下场景非常有用:
希望这篇教程能帮助你掌握 Rust半平面交算法的核心思想与实现技巧。如果你正在学习计算几何Rust实现,不妨动手敲一遍代码,加深理解!
关键词回顾:Rust半平面交算法、计算几何Rust实现、半平面交教程、Rust几何算法。
本文由主机测评网于2025-12-15发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://vpshk.cn/2025127958.html