fix(hatch): more improvement on arc boundary and pattern
This commit is contained in:
parent
461d0c5d86
commit
773a5f74cb
4 changed files with 159 additions and 93 deletions
|
|
@ -1,10 +1,12 @@
|
||||||
// HatchModel — CPU-side hatch fill data; rendered entirely on the GPU.
|
// HatchModel — CPU-side hatch fill data; rendered entirely on the GPU.
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
//
|
//
|
||||||
// The boundary is a closed polygon in world XY coordinates (max 64 vertices).
|
// The boundary is a closed polygon in world XY coordinates.
|
||||||
// The GPU fragment shader performs point-in-polygon and hatch-line tests so
|
// The GPU fragment shader performs point-in-polygon and hatch-line tests so
|
||||||
// no line geometry is ever tessellated on the CPU.
|
// no line geometry is ever tessellated on the CPU.
|
||||||
|
|
||||||
|
pub const MAX_HATCH_BOUNDARY_VERTS: usize = 1024;
|
||||||
|
|
||||||
/// One line family from a PAT-format hatch pattern.
|
/// One line family from a PAT-format hatch pattern.
|
||||||
///
|
///
|
||||||
/// Format mirrors the standard PAT line format:
|
/// Format mirrors the standard PAT line format:
|
||||||
|
|
@ -41,7 +43,7 @@ pub enum HatchPattern {
|
||||||
/// A hatched region defined by a closed polygon boundary.
|
/// A hatched region defined by a closed polygon boundary.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct HatchModel {
|
pub struct HatchModel {
|
||||||
/// World XY coordinates of the boundary polygon vertices (max 64).
|
/// World XY coordinates of the boundary polygon vertices.
|
||||||
/// Arc so HatchModel::clone() is a pointer bump rather than a full Vec copy.
|
/// Arc so HatchModel::clone() is a pointer bump rather than a full Vec copy.
|
||||||
pub boundary: Arc<Vec<[f32; 2]>>,
|
pub boundary: Arc<Vec<[f32; 2]>>,
|
||||||
/// Fill pattern.
|
/// Fill pattern.
|
||||||
|
|
|
||||||
151
src/scene/mod.rs
151
src/scene/mod.rs
|
|
@ -1916,19 +1916,35 @@ impl Scene {
|
||||||
|
|
||||||
fn hatch_model_from_dxf(dxf: &DxfHatch, color: [f32; 4], world_offset: [f64; 3]) -> Option<HatchModel> {
|
fn hatch_model_from_dxf(dxf: &DxfHatch, color: [f32; 4], world_offset: [f64; 3]) -> Option<HatchModel> {
|
||||||
let [ox, oy, _oz] = world_offset;
|
let [ox, oy, _oz] = world_offset;
|
||||||
let path = dxf
|
let normal = (dxf.normal.x, dxf.normal.y, dxf.normal.z);
|
||||||
.paths
|
let to_xy = |x: f64, y: f64| -> [f32; 2] {
|
||||||
.iter()
|
let (wx, wy, _) = crate::scene::transform::ocs_point_to_wcs(
|
||||||
.find(|p| p.flags.is_external())
|
(x, y, dxf.elevation),
|
||||||
.or_else(|| dxf.paths.first())?;
|
normal,
|
||||||
|
);
|
||||||
|
[(wx - ox) as f32, (wy - oy) as f32]
|
||||||
|
};
|
||||||
|
if dxf.paths.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let mut boundary: Vec<[f32; 2]> = Vec::new();
|
let mut boundary: Vec<[f32; 2]> = Vec::new();
|
||||||
|
|
||||||
for edge in &path.edges {
|
for path in &dxf.paths {
|
||||||
match edge {
|
let before_path = boundary.len();
|
||||||
|
if !boundary.is_empty() {
|
||||||
|
boundary.push([f32::NAN, f32::NAN]);
|
||||||
|
}
|
||||||
|
let path_start = boundary.len();
|
||||||
|
|
||||||
|
for edge in &path.edges {
|
||||||
|
match edge {
|
||||||
BoundaryEdge::Polyline(poly) => {
|
BoundaryEdge::Polyline(poly) => {
|
||||||
let verts = &poly.vertices;
|
let verts = &poly.vertices;
|
||||||
let count = verts.len();
|
let count = verts.len();
|
||||||
|
if count == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let seg_count = if poly.is_closed {
|
let seg_count = if poly.is_closed {
|
||||||
count
|
count
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1939,73 +1955,86 @@ impl Scene {
|
||||||
let v1 = &verts[(i + 1) % count];
|
let v1 = &verts[(i + 1) % count];
|
||||||
let bulge = v0.z;
|
let bulge = v0.z;
|
||||||
if bulge.abs() < 1e-9 {
|
if bulge.abs() < 1e-9 {
|
||||||
boundary.push([(v0.x - ox) as f32, (v0.y - oy) as f32]);
|
boundary.push(to_xy(v0.x, v0.y));
|
||||||
} else {
|
} else {
|
||||||
let p0 = [(v0.x - ox) as f32, (v0.y - oy) as f32];
|
let p0 = [v0.x as f32, v0.y as f32];
|
||||||
let p1 = [(v1.x - ox) as f32, (v1.y - oy) as f32];
|
let p1 = [v1.x as f32, v1.y as f32];
|
||||||
let angle = 4.0 * (bulge as f32).atan();
|
let theta = 4.0 * (bulge as f32).atan();
|
||||||
let dx = p1[0] - p0[0];
|
let dx = p1[0] - p0[0];
|
||||||
let dy = p1[1] - p0[1];
|
let dy = p1[1] - p0[1];
|
||||||
let d = (dx * dx + dy * dy).sqrt();
|
let d = (dx * dx + dy * dy).sqrt();
|
||||||
let r = (d / 2.0) / (angle / 2.0).sin().abs();
|
if d < 1e-9 {
|
||||||
|
boundary.push(to_xy(v0.x, v0.y));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let r = (d * 0.5) / (theta * 0.5).sin().abs();
|
||||||
let mx = (p0[0] + p1[0]) * 0.5;
|
let mx = (p0[0] + p1[0]) * 0.5;
|
||||||
let my = (p0[1] + p1[1]) * 0.5;
|
let my = (p0[1] + p1[1]) * 0.5;
|
||||||
let len = d.max(1e-9);
|
let px = -dy / d;
|
||||||
let px = -dy / len;
|
let py = dx / d;
|
||||||
let py = dx / len;
|
|
||||||
let sign = if bulge > 0.0 { 1.0_f32 } else { -1.0_f32 };
|
let sign = if bulge > 0.0 { 1.0_f32 } else { -1.0_f32 };
|
||||||
let h = r - (r * r - d * d / 4.0).max(0.0).sqrt();
|
let center_offset = r * (theta * 0.5).cos();
|
||||||
let cx = mx - sign * px * (r - h);
|
let cx = mx + sign * px * center_offset;
|
||||||
let cy = my - sign * py * (r - h);
|
let cy = my + sign * py * center_offset;
|
||||||
let a0 = (p0[1] - cy).atan2(p0[0] - cx);
|
let a0 = (p0[1] - cy).atan2(p0[0] - cx);
|
||||||
let a1 = (p1[1] - cy).atan2(p1[0] - cx);
|
let a1 = (p1[1] - cy).atan2(p1[0] - cx);
|
||||||
let (sa, mut ea) = if bulge > 0.0 { (a0, a1) } else { (a1, a0) };
|
let mut sweep = a1 - a0;
|
||||||
if ea < sa {
|
if bulge > 0.0 {
|
||||||
ea += std::f32::consts::TAU;
|
if sweep <= 0.0 {
|
||||||
|
sweep += std::f32::consts::TAU;
|
||||||
|
}
|
||||||
|
} else if sweep >= 0.0 {
|
||||||
|
sweep -= std::f32::consts::TAU;
|
||||||
}
|
}
|
||||||
let span = ea - sa;
|
if sweep.abs() < 1e-7 {
|
||||||
let segs = ((span.abs() / std::f32::consts::TAU) * 16.0)
|
sweep = if bulge > 0.0 {
|
||||||
|
std::f32::consts::TAU
|
||||||
|
} else {
|
||||||
|
-std::f32::consts::TAU
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let segs = ((sweep.abs() / std::f32::consts::TAU) * 16.0)
|
||||||
.ceil()
|
.ceil()
|
||||||
.max(4.0) as u32;
|
.max(4.0) as u32;
|
||||||
for j in 0..segs {
|
for j in 0..segs {
|
||||||
let t = sa + span * (j as f32 / segs as f32);
|
let a = a0 + sweep * (j as f32 / segs as f32);
|
||||||
boundary.push([cx + r * t.cos(), cy + r * t.sin()]);
|
boundary.push(to_xy(
|
||||||
|
(cx + r * a.cos()) as f64,
|
||||||
|
(cy + r * a.sin()) as f64,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if poly.is_closed {
|
if poly.is_closed {
|
||||||
if let Some(&first) = boundary.first() {
|
if let Some(&first) = boundary.get(path_start) {
|
||||||
boundary.push(first);
|
boundary.push(first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoundaryEdge::Line(line) => {
|
BoundaryEdge::Line(line) => {
|
||||||
boundary.push([(line.start.x - ox) as f32, (line.start.y - oy) as f32]);
|
boundary.push(to_xy(line.start.x, line.start.y));
|
||||||
boundary.push([(line.end.x - ox) as f32, (line.end.y - oy) as f32]);
|
boundary.push(to_xy(line.end.x, line.end.y));
|
||||||
}
|
}
|
||||||
BoundaryEdge::CircularArc(arc) => {
|
BoundaryEdge::CircularArc(arc) => {
|
||||||
let cx = (arc.center.x - ox) as f32;
|
let cx = arc.center.x;
|
||||||
let cy = (arc.center.y - oy) as f32;
|
let cy = arc.center.y;
|
||||||
let r = arc.radius as f32;
|
let r = arc.radius;
|
||||||
let (sa, ea) = if arc.counter_clockwise {
|
let sa = arc.start_angle;
|
||||||
(arc.start_angle as f32, arc.end_angle as f32)
|
let ea = arc.end_angle;
|
||||||
} else {
|
let end = if ea >= sa { ea } else { ea + std::f64::consts::TAU };
|
||||||
(arc.end_angle as f32, arc.start_angle as f32)
|
|
||||||
};
|
|
||||||
let mut end = ea;
|
|
||||||
if end < sa {
|
|
||||||
end += std::f32::consts::TAU;
|
|
||||||
}
|
|
||||||
let span = end - sa;
|
let span = end - sa;
|
||||||
let segs = ((span / std::f32::consts::TAU) * 32.0).ceil().max(4.0) as u32;
|
let segs = ((span / std::f64::consts::TAU) * 32.0).ceil().max(4.0) as u32;
|
||||||
for i in 0..=segs {
|
for i in 0..=segs {
|
||||||
let t = sa + span * (i as f32 / segs as f32);
|
let t = sa + span * (i as f64 / segs as f64);
|
||||||
boundary.push([cx + r * t.cos(), cy + r * t.sin()]);
|
boundary.push(to_xy(
|
||||||
|
cx + r * t.cos(),
|
||||||
|
cy + r * t.sin(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoundaryEdge::EllipticArc(ell) => {
|
BoundaryEdge::EllipticArc(ell) => {
|
||||||
let cx = (ell.center.x - ox) as f32;
|
let cx = ell.center.x;
|
||||||
let cy = (ell.center.y - oy) as f32;
|
let cy = ell.center.y;
|
||||||
let maj_x = ell.major_axis_endpoint.x as f32;
|
let maj_x = ell.major_axis_endpoint.x as f32;
|
||||||
let maj_y = ell.major_axis_endpoint.y as f32;
|
let maj_y = ell.major_axis_endpoint.y as f32;
|
||||||
let r_maj = (maj_x * maj_x + maj_y * maj_y).sqrt();
|
let r_maj = (maj_x * maj_x + maj_y * maj_y).sqrt();
|
||||||
|
|
@ -2026,10 +2055,10 @@ impl Scene {
|
||||||
let t = sa + span * (i as f32 / segs as f32);
|
let t = sa + span * (i as f32 / segs as f32);
|
||||||
let lx = r_maj * t.cos();
|
let lx = r_maj * t.cos();
|
||||||
let ly = r_min * t.sin();
|
let ly = r_min * t.sin();
|
||||||
boundary.push([
|
boundary.push(to_xy(
|
||||||
cx + lx * rot.cos() - ly * rot.sin(),
|
cx + (lx * rot.cos() - ly * rot.sin()) as f64,
|
||||||
cy + lx * rot.sin() + ly * rot.cos(),
|
cy + (lx * rot.sin() + ly * rot.cos()) as f64,
|
||||||
]);
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoundaryEdge::Spline(spline) => {
|
BoundaryEdge::Spline(spline) => {
|
||||||
|
|
@ -2057,21 +2086,34 @@ impl Scene {
|
||||||
for i in 0..=segs {
|
for i in 0..=segs {
|
||||||
let t = t0 + (t1 - t0) * (i as f64 / segs as f64);
|
let t = t0 + (t1 - t0) * (i as f64 / segs as f64);
|
||||||
let p = bspl.subs(t);
|
let p = bspl.subs(t);
|
||||||
boundary.push([(p.x - ox) as f32, (p.y - oy) as f32]);
|
boundary.push(to_xy(p.x, p.y));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for cp in &spline.control_points {
|
for cp in &spline.control_points {
|
||||||
boundary.push([(cp.x - ox) as f32, (cp.y - oy) as f32]);
|
boundary.push(to_xy(cp.x, cp.y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if boundary.len() == path_start {
|
||||||
|
boundary.truncate(before_path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if boundary.len() >= path_start + 3 {
|
||||||
|
let first = boundary[path_start];
|
||||||
|
let last = *boundary.last().unwrap();
|
||||||
|
if (first[0] - last[0]).abs() > 1e-5 || (first[1] - last[1]).abs() > 1e-5 {
|
||||||
|
boundary.push(first);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if boundary.is_empty() {
|
if boundary.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
boundary.truncate(64);
|
boundary.truncate(hatch_model::MAX_HATCH_BOUNDARY_VERTS);
|
||||||
|
|
||||||
let pattern = if dxf.gradient_color.is_enabled() {
|
let pattern = if dxf.gradient_color.is_enabled() {
|
||||||
let color2 = dxf
|
let color2 = dxf
|
||||||
|
|
@ -2113,8 +2155,8 @@ impl Scene {
|
||||||
pattern,
|
pattern,
|
||||||
name,
|
name,
|
||||||
color,
|
color,
|
||||||
angle_offset: 0.0,
|
angle_offset: dxf.pattern_angle as f32,
|
||||||
scale: 1.0,
|
scale: dxf.pattern_scale as f32,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3683,4 +3725,3 @@ fn polyline_segment_fill(
|
||||||
Some(boundary)
|
Some(boundary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@
|
||||||
//
|
//
|
||||||
// Group 1 bindings per hatch:
|
// Group 1 bindings per hatch:
|
||||||
// binding 0 — HatchUniformData (64 bytes) mode, color, angle_offset, scale, gradient params
|
// binding 0 — HatchUniformData (64 bytes) mode, color, angle_offset, scale, gradient params
|
||||||
// binding 1 — BoundaryData (1024 bytes) up to 64 boundary polygon vertices
|
// binding 1 — BoundaryData (16384 bytes) boundary polygon vertices
|
||||||
// binding 2 — FamilyBatchData (1296 bytes) up to 16 line families + 128 dash values
|
// binding 2 — FamilyBatchData (1296 bytes) up to 16 line families + 128 dash values
|
||||||
|
|
||||||
use crate::scene::hatch_model::{HatchModel, HatchPattern, PatFamily};
|
use crate::scene::hatch_model::{
|
||||||
|
HatchModel, HatchPattern, PatFamily, MAX_HATCH_BOUNDARY_VERTS,
|
||||||
|
};
|
||||||
use iced::wgpu;
|
use iced::wgpu;
|
||||||
use iced::wgpu::util::DeviceExt;
|
use iced::wgpu::util::DeviceExt;
|
||||||
|
|
||||||
|
|
@ -64,7 +66,7 @@ pub struct HatchUniformData {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
pub struct BoundaryData {
|
pub struct BoundaryData {
|
||||||
pub verts: [[f32; 4]; 64], // world XY in .xy, .zw unused
|
pub verts: [[f32; 4]; MAX_HATCH_BOUNDARY_VERTS], // world XY in .xy, .zw unused
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One line family packed for the shader (48 bytes).
|
/// One line family packed for the shader (48 bytes).
|
||||||
|
|
@ -120,26 +122,21 @@ impl HatchGpu {
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── Bounding box ─────────────────────────────────────────────────
|
// ── Bounding box ─────────────────────────────────────────────────
|
||||||
let min_x = model
|
let (mut min_x, mut max_x, mut min_y, mut max_y) = (
|
||||||
.boundary
|
f32::INFINITY,
|
||||||
.iter()
|
f32::NEG_INFINITY,
|
||||||
.map(|v| v[0])
|
f32::INFINITY,
|
||||||
.fold(f32::INFINITY, f32::min);
|
f32::NEG_INFINITY,
|
||||||
let max_x = model
|
);
|
||||||
.boundary
|
for &[x, y] in model.boundary.iter() {
|
||||||
.iter()
|
if !x.is_finite() || !y.is_finite() {
|
||||||
.map(|v| v[0])
|
continue;
|
||||||
.fold(f32::NEG_INFINITY, f32::max);
|
}
|
||||||
let min_y = model
|
min_x = min_x.min(x);
|
||||||
.boundary
|
max_x = max_x.max(x);
|
||||||
.iter()
|
min_y = min_y.min(y);
|
||||||
.map(|v| v[1])
|
max_y = max_y.max(y);
|
||||||
.fold(f32::INFINITY, f32::min);
|
}
|
||||||
let max_y = model
|
|
||||||
.boundary
|
|
||||||
.iter()
|
|
||||||
.map(|v| v[1])
|
|
||||||
.fold(f32::NEG_INFINITY, f32::max);
|
|
||||||
|
|
||||||
let max_spacing = match &model.pattern {
|
let max_spacing = match &model.pattern {
|
||||||
HatchPattern::Pattern(families) => families
|
HatchPattern::Pattern(families) => families
|
||||||
|
|
@ -189,17 +186,22 @@ impl HatchGpu {
|
||||||
let projs: Vec<f32> = model
|
let projs: Vec<f32> = model
|
||||||
.boundary
|
.boundary
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|v| v[0].is_finite() && v[1].is_finite())
|
||||||
.map(|&[x, y]| x * grad_cos + y * grad_sin)
|
.map(|&[x, y]| x * grad_cos + y * grad_sin)
|
||||||
.collect();
|
.collect();
|
||||||
let proj_min = projs.iter().cloned().fold(f32::INFINITY, f32::min);
|
if projs.is_empty() {
|
||||||
let proj_max = projs.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
|
(0.0, 1.0)
|
||||||
(proj_min, (proj_max - proj_min).max(1e-4))
|
} else {
|
||||||
|
let proj_min = projs.iter().cloned().fold(f32::INFINITY, f32::min);
|
||||||
|
let proj_max = projs.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
|
||||||
|
(proj_min, (proj_max - proj_min).max(1e-4))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
(0.0, 1.0)
|
(0.0, 1.0)
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── HatchUniformData ─────────────────────────────────────────────
|
// ── HatchUniformData ─────────────────────────────────────────────
|
||||||
let n = model.boundary.len().min(64);
|
let n = model.boundary.len().min(MAX_HATCH_BOUNDARY_VERTS);
|
||||||
let uniform_data = HatchUniformData {
|
let uniform_data = HatchUniformData {
|
||||||
color: model.color,
|
color: model.color,
|
||||||
color2,
|
color2,
|
||||||
|
|
@ -215,9 +217,14 @@ impl HatchGpu {
|
||||||
|
|
||||||
// ── BoundaryData ─────────────────────────────────────────────────
|
// ── BoundaryData ─────────────────────────────────────────────────
|
||||||
let mut boundary_data = BoundaryData {
|
let mut boundary_data = BoundaryData {
|
||||||
verts: [[0.0; 4]; 64],
|
verts: [[0.0; 4]; MAX_HATCH_BOUNDARY_VERTS],
|
||||||
};
|
};
|
||||||
for (i, &[x, y]) in model.boundary.iter().take(64).enumerate() {
|
for (i, &[x, y]) in model
|
||||||
|
.boundary
|
||||||
|
.iter()
|
||||||
|
.take(MAX_HATCH_BOUNDARY_VERTS)
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
boundary_data.verts[i] = [x, y, 0.0, 0.0];
|
boundary_data.verts[i] = [x, y, 0.0, 0.0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ struct HatchUniforms {
|
||||||
@group(1) @binding(0) var<uniform> h: HatchUniforms;
|
@group(1) @binding(0) var<uniform> h: HatchUniforms;
|
||||||
|
|
||||||
struct Boundary {
|
struct Boundary {
|
||||||
verts: array<vec4<f32>, 64>,
|
verts: array<vec4<f32>, 1024>,
|
||||||
}
|
}
|
||||||
@group(1) @binding(1) var<uniform> b: Boundary;
|
@group(1) @binding(1) var<uniform> b: Boundary;
|
||||||
|
|
||||||
|
|
@ -87,18 +87,34 @@ struct VOut {
|
||||||
|
|
||||||
// ── Point-in-polygon (ray casting) ────────────────────────────────────────
|
// ── Point-in-polygon (ray casting) ────────────────────────────────────────
|
||||||
|
|
||||||
|
fn valid_vertex(p: vec2<f32>) -> bool {
|
||||||
|
return p.x == p.x && p.y == p.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edge_crosses(p: vec2<f32>, a: vec2<f32>, c: vec2<f32>) -> bool {
|
||||||
|
if (a.y > p.y) != (c.y > p.y) {
|
||||||
|
let x_int = (c.x - a.x) * (p.y - a.y) / (c.y - a.y) + a.x;
|
||||||
|
return p.x < x_int;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
fn in_polygon(p: vec2<f32>) -> bool {
|
fn in_polygon(p: vec2<f32>) -> bool {
|
||||||
var inside = false;
|
var inside = false;
|
||||||
let n = h.vcount;
|
let n = h.vcount;
|
||||||
var j = n - 1u;
|
var prev = vec2<f32>(0.0, 0.0);
|
||||||
|
var have_prev = false;
|
||||||
for (var i = 0u; i < n; i++) {
|
for (var i = 0u; i < n; i++) {
|
||||||
let vi = b.verts[i].xy;
|
let vi = b.verts[i].xy;
|
||||||
let vj = b.verts[j].xy;
|
if !valid_vertex(vi) {
|
||||||
if (vi.y > p.y) != (vj.y > p.y) {
|
have_prev = false;
|
||||||
let x_int = (vj.x - vi.x) * (p.y - vi.y) / (vj.y - vi.y) + vi.x;
|
continue;
|
||||||
if p.x < x_int { inside = !inside; }
|
|
||||||
}
|
}
|
||||||
j = i;
|
if have_prev && edge_crosses(p, prev, vi) {
|
||||||
|
inside = !inside;
|
||||||
|
}
|
||||||
|
prev = vi;
|
||||||
|
have_prev = true;
|
||||||
}
|
}
|
||||||
return inside;
|
return inside;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue