forked from 1b-agent/opencadstudio-upstream
Merge pull request #1240 from kevin9327/fix/step-export-large-coordinates
This commit is contained in:
commit
3fe91fa6e1
1 file changed
+75
-7
+75
-7
@@ -21,7 +21,7 @@ use std::fmt::Write as FmtWrite;
|
||||
pub fn build_step(meshes: &[&MeshModel]) -> Option<String> {
|
||||
// Collect all triangles as (v0, v1, v2, normal).
|
||||
struct Tri {
|
||||
v: [[f32; 3]; 3],
|
||||
v: [[f64; 3]; 3],
|
||||
n: [f32; 3],
|
||||
}
|
||||
|
||||
@@ -30,6 +30,13 @@ pub fn build_step(meshes: &[&MeshModel]) -> Option<String> {
|
||||
let verts = &mesh.verts;
|
||||
let normals = &mesh.normals;
|
||||
let idx = &mesh.indices;
|
||||
// Positions are stored as an f32 half plus a low residual so they stay
|
||||
// precise at survey coordinates; write the sum, as the renderer uses.
|
||||
let point = |i: usize| {
|
||||
let high = verts[i];
|
||||
let low = mesh.verts_low.get(i).copied().unwrap_or([0.0; 3]);
|
||||
[0, 1, 2].map(|k| high[k] as f64 + low[k] as f64)
|
||||
};
|
||||
let n_tri = idx.len() / 3;
|
||||
for t in 0..n_tri {
|
||||
let i0 = idx[t * 3] as usize;
|
||||
@@ -38,9 +45,9 @@ pub fn build_step(meshes: &[&MeshModel]) -> Option<String> {
|
||||
if i0 >= verts.len() || i1 >= verts.len() || i2 >= verts.len() {
|
||||
continue;
|
||||
}
|
||||
let a = verts[i0];
|
||||
let b = verts[i1];
|
||||
let c = verts[i2];
|
||||
let a = point(i0);
|
||||
let b = point(i1);
|
||||
let c = point(i2);
|
||||
let n = if !normals.is_empty() && i0 < normals.len() {
|
||||
normals[i0]
|
||||
} else {
|
||||
@@ -49,8 +56,8 @@ pub fn build_step(meshes: &[&MeshModel]) -> Option<String> {
|
||||
let nx = ab[1] * ac[2] - ab[2] * ac[1];
|
||||
let ny = ab[2] * ac[0] - ab[0] * ac[2];
|
||||
let nz = ab[0] * ac[1] - ab[1] * ac[0];
|
||||
let len = (nx * nx + ny * ny + nz * nz).sqrt().max(f32::EPSILON);
|
||||
[nx / len, ny / len, nz / len]
|
||||
let len = (nx * nx + ny * ny + nz * nz).sqrt().max(f64::EPSILON);
|
||||
[(nx / len) as f32, (ny / len) as f32, (nz / len) as f32]
|
||||
};
|
||||
tris.push(Tri { v: [a, b, c], n });
|
||||
}
|
||||
@@ -128,7 +135,7 @@ pub fn build_step(meshes: &[&MeshModel]) -> Option<String> {
|
||||
tri.v[k1][1] - tri.v[k][1],
|
||||
tri.v[k1][2] - tri.v[k][2],
|
||||
];
|
||||
let len = (dx * dx + dy * dy + dz * dz).sqrt().max(f32::EPSILON);
|
||||
let len = (dx * dx + dy * dy + dz * dz).sqrt().max(f64::EPSILON);
|
||||
writeln!(
|
||||
data,
|
||||
"#{} = DIRECTION('',({:.6},{:.6},{:.6}));",
|
||||
@@ -263,3 +270,64 @@ fn chrono_timestamp() -> String {
|
||||
let day = doy % 30 + 1;
|
||||
format!("{year:04}-{month:02}-{day:02}T{hh:02}:{mm:02}:{ss:02}")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::build_step;
|
||||
use crate::scene::model::mesh_model::MeshModel;
|
||||
|
||||
fn triangle(verts: Vec<[f32; 3]>, verts_low: Vec<[f32; 3]>) -> MeshModel {
|
||||
MeshModel {
|
||||
name: String::new(),
|
||||
verts,
|
||||
verts_low,
|
||||
normals: Vec::new(),
|
||||
indices: vec![0, 1, 2],
|
||||
triangle_material_handles: Vec::new(),
|
||||
triangle_colors: Vec::new(),
|
||||
color: [1.0; 4],
|
||||
selected: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Split an absolute position into the f32 pair the tessellator stores.
|
||||
fn split(point: [f64; 3]) -> ([f32; 3], [f32; 3]) {
|
||||
let high = point.map(|v| v as f32);
|
||||
let low = [0, 1, 2].map(|i| (point[i] - high[i] as f64) as f32);
|
||||
(high, low)
|
||||
}
|
||||
|
||||
/// At survey coordinates the f32 half alone is 6 cm coarse: 500000.123
|
||||
/// is stored as 500000.125 plus a -0.002 residual. The export has to add
|
||||
/// the residual back, as the renderer does.
|
||||
#[test]
|
||||
fn large_coordinates_keep_their_low_residual() {
|
||||
let (verts, verts_low): (Vec<_>, Vec<_>) = [
|
||||
[500000.123, 500010.456, 0.0],
|
||||
[500001.123, 500010.456, 0.0],
|
||||
[500000.123, 500011.456, 0.0],
|
||||
]
|
||||
.into_iter()
|
||||
.map(split)
|
||||
.unzip();
|
||||
let step = build_step(&[&triangle(verts, verts_low)]).expect("step");
|
||||
assert!(
|
||||
step.contains("CARTESIAN_POINT('',(500000.123000,500010.456000,0.000000));"),
|
||||
"first vertex lost its residual:\n{}",
|
||||
step.lines()
|
||||
.find(|line| line.contains("CARTESIAN_POINT"))
|
||||
.unwrap_or("")
|
||||
);
|
||||
}
|
||||
|
||||
/// Meshes without residuals (imported OBJ, legacy meshes) export as before.
|
||||
#[test]
|
||||
fn a_mesh_without_low_residuals_exports_its_positions() {
|
||||
let mesh = triangle(
|
||||
vec![[1.5, 2.0, 0.0], [2.5, 2.0, 0.0], [1.5, 3.0, 0.0]],
|
||||
Vec::new(),
|
||||
);
|
||||
let step = build_step(&[&mesh]).expect("step");
|
||||
assert!(step.contains("CARTESIAN_POINT('',(1.500000,2.000000,0.000000));"));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user