feat: add circle drawing support in path planner

This commit is contained in:
xin
2026-06-18 15:16:11 +08:00
parent c16d3d8586
commit 703b4ff865
11 changed files with 119 additions and 52 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "spectral-insight-mission-plan"
version = "0.0.2"
version = "0.0.3"
description = "Spectral Insight Mission Plan"
authors = ["you"]
edition = "2021"

View File

@ -27,9 +27,11 @@ pub fn estimate_scan_time_minutes(params: ScanParams) -> f64 {
services::path_generator::estimate_scan_time(&params)
}
#[command]
pub fn generate_scan_paths_multi(
regions: Vec<ScanRegion>,
shapes: Vec<String>,
camera: CameraParams,
coverage_rate: f64,
speed_y_cm_s: f64,
@ -38,7 +40,7 @@ pub fn generate_scan_paths_multi(
mode: ScanMode,
) -> Result<PathLineFile, AppError> {
let records = services::path_generator::generate_paths_multi(
&regions, &camera, coverage_rate,
&regions, &shapes, &camera, coverage_rate,
speed_y_cm_s, speed_x_start_cm_s, speed_x_scan_cm_s, &mode,
);
let count = records.len() as u64;
@ -48,6 +50,7 @@ pub fn generate_scan_paths_multi(
#[command]
pub fn estimate_scan_time_multi_minutes(
regions: Vec<ScanRegion>,
shapes: Vec<String>,
camera: CameraParams,
coverage_rate: f64,
speed_y_cm_s: f64,
@ -56,7 +59,7 @@ pub fn estimate_scan_time_multi_minutes(
mode: ScanMode,
) -> f64 {
services::path_generator::estimate_scan_time_multi(
&regions, &camera, coverage_rate,
&regions, &shapes, &camera, coverage_rate,
speed_y_cm_s, speed_x_start_cm_s, speed_x_scan_cm_s, &mode,
)
}

View File

@ -103,8 +103,10 @@ pub fn estimate_scan_time(params: &ScanParams) -> f64 {
}
/// Generate paths for multiple regions, concatenated into one record list.
pub fn generate_paths_multi(
regions: &[ScanRegion],
shapes: &[String],
camera: &crate::models::CameraParams,
coverage_rate: f64,
speed_y_cm_s: f64,
@ -113,24 +115,54 @@ pub fn generate_paths_multi(
mode: &ScanMode,
) -> Vec<PathLineRecord> {
let mut all_records = Vec::new();
for region in regions {
let params = ScanParams {
region: region.clone(),
camera: camera.clone(),
coverage_rate,
speed_y_cm_s,
speed_x_start_cm_s,
speed_x_scan_cm_s,
mode: mode.clone(),
};
all_records.extend(generate_path(&params));
for (i, region) in regions.iter().enumerate() {
let is_circle = shapes.get(i).map(|s| s == "Circle").unwrap_or(false);
let step = calc_footprint(camera.height_cm, camera.fov_degrees)
* (1.0 - coverage_rate / 100.0);
let line_count = if step <= 0.0 { 1 } else { ((region.y_max - region.y_min) / step).ceil() as u64 + 1 };
for li in 0..line_count {
let y = region.y_min + (li as f64) * step;
if y > region.y_max { break; }
let (x_start, x_end) = if is_circle {
let cx = (region.x_min + region.x_max) / 2.0;
let cy = (region.y_min + region.y_max) / 2.0;
let rx = (region.x_max - region.x_min) / 2.0;
let ry = (region.y_max - region.y_min) / 2.0;
// Ellipse: ((x-cx)/rx)^2 + ((y-cy)/ry)^2 = 1
let dy = (y - cy) / ry;
if dy.abs() > 1.0 { continue; } // outside circle
let half_w = rx * (1.0 - dy * dy).sqrt();
(cx - half_w, cx + half_w)
} else {
(region.x_min, region.x_max)
};
let is_forward = li % 2 == 0;
let (scan_start, scan_end) = match mode {
ScanMode::Zigzag => {
if is_forward { (x_start, x_end) } else { (x_end, x_start) }
}
ScanMode::OneWay => (x_start, x_end),
};
all_records.push(PathLineRecord {
target_y_position: y,
speed_target_y_position: speed_y_cm_s,
target_x_min_position: scan_start,
speed_target_x_min_position: speed_x_start_cm_s,
target_x_max_position: scan_end,
speed_target_x_max_position: speed_x_scan_cm_s,
});
}
}
all_records
}
/// Estimate total time for multiple regions.
pub fn estimate_scan_time_multi(
regions: &[ScanRegion],
shapes: &[String],
camera: &crate::models::CameraParams,
coverage_rate: f64,
speed_y_cm_s: f64,
@ -138,18 +170,13 @@ pub fn estimate_scan_time_multi(
speed_x_scan_cm_s: f64,
mode: &ScanMode,
) -> f64 {
let mut total = 0.0;
for region in regions {
let params = ScanParams {
region: region.clone(),
camera: camera.clone(),
coverage_rate,
speed_y_cm_s,
speed_x_start_cm_s,
speed_x_scan_cm_s,
mode: mode.clone(),
};
total += estimate_scan_time(&params);
}
total
let records = generate_paths_multi(regions, shapes, camera, coverage_rate,
speed_y_cm_s, speed_x_start_cm_s, speed_x_scan_cm_s, mode);
if records.is_empty() { return 0.0; }
let total_x = records.len() as f64 * (regions.iter().map(|r| (r.x_max - r.x_min).abs()).sum::<f64>() / regions.len() as f64) / speed_x_scan_cm_s;
let return_time = if matches!(mode, ScanMode::OneWay) {
(records.len() as f64 - 1.0) * speed_x_scan_cm_s / speed_x_start_cm_s.max(0.01)
} else { 0.0 };
(total_x + return_time) / 60.0
}

View File

@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Spectral Insight Mission Plan",
"version": "0.0.2",
"version": "0.0.3",
"identifier": "com.spectral-insight.mission-plan",
"build": {
"beforeDevCommand": "npm run dev",