forked from 1b-agent/opencadstudio-upstream
fix(point): preserve relative marker sizing
Keep rendering, picking, block transforms, and plotted output on the same viewport-relative PDSIZE geometry without overloading wire width fields.
This commit is contained in:
1 parent
c5fe70627a
commit
7293bec1b9
69 files changed
+823
-221
No files matched your search
+125
-13
@@ -49,6 +49,26 @@ pub enum TangentGeom {
|
||||
},
|
||||
}
|
||||
|
||||
/// Viewport-relative display transform for a point marker.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct PointMarker {
|
||||
pub origin: glam::DVec3,
|
||||
pub normal: glam::DVec3,
|
||||
pub axis_x: glam::DVec3,
|
||||
pub axis_y: glam::DVec3,
|
||||
pub viewport_percent: f32,
|
||||
}
|
||||
|
||||
impl PointMarker {
|
||||
pub fn resolve(self, point: glam::DVec3, view_height: f64) -> glam::DVec3 {
|
||||
let normal = self.normal.normalize_or(glam::DVec3::Z);
|
||||
let delta = point - self.origin;
|
||||
let axial = normal * delta.dot(normal);
|
||||
let scale = self.viewport_percent.max(0.0) as f64 * 0.01 * view_height.max(0.0);
|
||||
self.origin + axial + (delta - axial) * scale
|
||||
}
|
||||
}
|
||||
|
||||
/// A 1-D entity (line, arc, polyline) represented as an ordered set of
|
||||
/// world-space points rendered as a quad strip (TriangleList).
|
||||
///
|
||||
@@ -66,6 +86,8 @@ pub struct WireModel {
|
||||
/// "all-zero residual" (interactive draw / preview wires whose coordinates
|
||||
/// don't need sub-f32 precision). Tessellation from CAD f64 fills it.
|
||||
pub points_low: Vec<[f32; 3]>,
|
||||
/// Point-marker scaling shared by the GPU, picking, and plotting paths.
|
||||
pub point_marker: Option<PointMarker>,
|
||||
/// RGBA colour in [0, 1].
|
||||
pub color: [f32; 4],
|
||||
/// Whether this wire is currently selected.
|
||||
@@ -225,6 +247,7 @@ impl WireModel {
|
||||
/// Create a solid wire (no dash pattern, 1px weight).
|
||||
pub fn solid(name: String, points: Vec<[f32; 3]>, color: [f32; 4], selected: bool) -> Self {
|
||||
Self {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
@@ -263,10 +286,11 @@ impl WireModel {
|
||||
out.name = format!("preview_{}", self.name);
|
||||
out.color = Self::CYAN;
|
||||
out.selected = false;
|
||||
for p in &mut out.points {
|
||||
p[0] += delta.x;
|
||||
p[1] += delta.y;
|
||||
p[2] += delta.z;
|
||||
map_points(&mut out.points, &mut out.points_low, |point| {
|
||||
point + delta.as_dvec3()
|
||||
});
|
||||
if let Some(marker) = &mut out.point_marker {
|
||||
marker.origin += delta.as_dvec3();
|
||||
}
|
||||
if !out.text_verts.is_empty() {
|
||||
let (dx, dy, dz) = (delta.x as f64, delta.y as f64, delta.z as f64);
|
||||
@@ -301,6 +325,17 @@ impl WireModel {
|
||||
for p in &mut out.points_low {
|
||||
*p = (rotation * glam::Vec3::from_array(*p)).to_array();
|
||||
}
|
||||
if let Some(marker) = &mut out.point_marker {
|
||||
let center = center.as_dvec3();
|
||||
let rotation = glam::DQuat::from_axis_angle(
|
||||
axis.as_dvec3().normalize_or(glam::DVec3::Z),
|
||||
angle_rad as f64,
|
||||
);
|
||||
marker.origin = center + rotation * (marker.origin - center);
|
||||
marker.normal = (rotation * marker.normal).normalize_or(glam::DVec3::Z);
|
||||
marker.axis_x = (rotation * marker.axis_x).normalize_or(glam::DVec3::X);
|
||||
marker.axis_y = (rotation * marker.axis_y).normalize_or(glam::DVec3::Y);
|
||||
}
|
||||
if !out.text_verts.is_empty() {
|
||||
let center = center.as_dvec3();
|
||||
let axis = axis.as_dvec3().normalize_or_zero();
|
||||
@@ -323,10 +358,23 @@ impl WireModel {
|
||||
out.name = format!("preview_{}", self.name);
|
||||
out.color = Self::CYAN;
|
||||
out.selected = false;
|
||||
for p in &mut out.points {
|
||||
p[0] = center.x + (p[0] - center.x) * factor;
|
||||
p[1] = center.y + (p[1] - center.y) * factor;
|
||||
p[2] = center.z + (p[2] - center.z) * factor;
|
||||
if let Some(marker) = out.point_marker {
|
||||
let center = center.as_dvec3();
|
||||
let normal = marker.normal.normalize_or(glam::DVec3::Z);
|
||||
let target_origin = center + (marker.origin - center) * factor as f64;
|
||||
map_points(&mut out.points, &mut out.points_low, |point| {
|
||||
let delta = point - marker.origin;
|
||||
let axial = normal * delta.dot(normal);
|
||||
target_origin + (delta - axial) + axial * factor as f64
|
||||
});
|
||||
if let Some(target) = &mut out.point_marker {
|
||||
target.origin = target_origin;
|
||||
}
|
||||
} else {
|
||||
let center = center.as_dvec3();
|
||||
map_points(&mut out.points, &mut out.points_low, |point| {
|
||||
center + (point - center) * factor as f64
|
||||
});
|
||||
}
|
||||
if !out.text_verts.is_empty() {
|
||||
let (cx, cy, cz) = (center.x as f64, center.y as f64, center.z as f64);
|
||||
@@ -373,11 +421,22 @@ pub fn stretched_windows(
|
||||
})
|
||||
};
|
||||
|
||||
for p in &mut out.points {
|
||||
if inside(p[0], p[1]) {
|
||||
p[0] += delta.x;
|
||||
p[1] += delta.y;
|
||||
p[2] += delta.z;
|
||||
if let Some(marker) = out.point_marker {
|
||||
if inside(marker.origin.x as f32, marker.origin.y as f32) {
|
||||
map_points(&mut out.points, &mut out.points_low, |point| {
|
||||
point + delta.as_dvec3()
|
||||
});
|
||||
if let Some(target) = &mut out.point_marker {
|
||||
target.origin += delta.as_dvec3();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for p in &mut out.points {
|
||||
if inside(p[0], p[1]) {
|
||||
p[0] += delta.x;
|
||||
p[1] += delta.y;
|
||||
p[2] += delta.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,6 +497,20 @@ pub fn stretched_windows(
|
||||
let point = glam::Vec3::from_array(*p);
|
||||
*p = (point - 2.0 * plane_normal.dot(point) * plane_normal).to_array();
|
||||
}
|
||||
if let Some(marker) = &mut out.point_marker {
|
||||
let p1 = p1.as_dvec3();
|
||||
let normal = plane_normal.as_dvec3();
|
||||
marker.origin -= 2.0 * normal.dot(marker.origin - p1) * normal;
|
||||
marker.normal =
|
||||
(marker.normal - 2.0 * normal.dot(marker.normal) * normal)
|
||||
.normalize_or(glam::DVec3::Z);
|
||||
marker.axis_x =
|
||||
(marker.axis_x - 2.0 * normal.dot(marker.axis_x) * normal)
|
||||
.normalize_or(glam::DVec3::X);
|
||||
marker.axis_y =
|
||||
(marker.axis_y - 2.0 * normal.dot(marker.axis_y) * normal)
|
||||
.normalize_or(glam::DVec3::Y);
|
||||
}
|
||||
// Glyph quads reflect wholesale (true mirror) — the caller only routes
|
||||
// text through here for MIRRTEXT-on; MIRRTEXT-off relocates via
|
||||
// `translated` so glyphs stay readable.
|
||||
@@ -466,6 +539,44 @@ pub fn stretched_windows(
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
pub fn point_world(&self, index: usize, view_height: f64) -> glam::DVec3 {
|
||||
let high = self.points[index];
|
||||
let low = self.points_low.get(index).copied().unwrap_or([0.0; 3]);
|
||||
let point = glam::DVec3::new(
|
||||
high[0] as f64 + low[0] as f64,
|
||||
high[1] as f64 + low[1] as f64,
|
||||
high[2] as f64 + low[2] as f64,
|
||||
);
|
||||
self.point_marker
|
||||
.map_or(point, |marker| marker.resolve(point, view_height))
|
||||
}
|
||||
}
|
||||
|
||||
fn map_points(
|
||||
points: &mut [[f32; 3]],
|
||||
points_low: &mut Vec<[f32; 3]>,
|
||||
map: impl Fn(glam::DVec3) -> glam::DVec3,
|
||||
) {
|
||||
let old_low = std::mem::take(points_low);
|
||||
points_low.reserve(points.len());
|
||||
for (index, high) in points.iter_mut().enumerate() {
|
||||
if !high[0].is_finite() || !high[1].is_finite() || !high[2].is_finite() {
|
||||
points_low.push([0.0; 3]);
|
||||
continue;
|
||||
}
|
||||
let low = old_low.get(index).copied().unwrap_or([0.0; 3]);
|
||||
let mapped = map(glam::DVec3::new(
|
||||
high[0] as f64 + low[0] as f64,
|
||||
high[1] as f64 + low[1] as f64,
|
||||
high[2] as f64 + low[2] as f64,
|
||||
));
|
||||
let (hx, lx) = WireModel::split_ds(mapped.x);
|
||||
let (hy, ly) = WireModel::split_ds(mapped.y);
|
||||
let (hz, lz) = WireModel::split_ds(mapped.z);
|
||||
*high = [hx, hy, hz];
|
||||
points_low.push([lx, ly, lz]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Map every glyph vertex's double-single world position through `f`, re-
|
||||
@@ -502,6 +613,7 @@ pub(crate) fn map_text_verts(
|
||||
impl Default for WireModel {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
point_marker: None,
|
||||
text_verts: Vec::new(),
|
||||
name: String::new(),
|
||||
points: Vec::new(),
|
||||
|
||||
Reference in new issue
Block a user