forked from 1b-agent/fieldcraft-upstream
Mesh: the swept cross-section's quad mesh as its own module
The schemes, interval constraints, curve nodes and region meshes of a multi-level sweep's cross-section move out of layered::mesh into hex::section, with the curve placement (seeds, projection onto walls) passed in, so the cross-section can be meshed on its own. region::mesh_structured gives a scheme's mesh or None where it would fold, without falling back to the free mesher. No change in behaviour. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
1 parent
c83b803f63
commit
a0b4a69452
4 files changed
+174
-112
No files matched your search
+17
-106
@@ -18,8 +18,9 @@
|
|||||||
//! region fills is decided by the inside test at its centre ([`super::geom::Inside`]).
|
//! region fills is decided by the inside test at its centre ([`super::geom::Inside`]).
|
||||||
//!
|
//!
|
||||||
//! **Meshing.** Interval assignment ([`super::interval`]) gives every curve its count so that
|
//! **Meshing.** Interval assignment ([`super::interval`]) gives every curve its count so that
|
||||||
//! four-sided regions are mapped (opposite sides equal) and the others have even loops for the
|
//! every region's scheme fits ([`super::section`]: four-sided regions mapped with opposite sides
|
||||||
//! free all-quad mesher; each level interval gets its own layer count. Each region's quads are
|
//! equal, three-sided ones the triangle primitive, rings and disks O-grids, the rest even loops
|
||||||
|
//! for the free all-quad mesher); each level interval gets its own layer count. Each region's quads are
|
||||||
//! stacked through its intervals; shared curves and levels share nodes, so the hexahedra
|
//! stacked through its intervals; shared curves and levels share nodes, so the hexahedra
|
||||||
//! conform. Nodes of curves on analytic walls are projected onto them; in a revolution, the
|
//! conform. Nodes of curves on analytic walls are projected onto them; in a revolution, the
|
||||||
//! rotated nodes then lie exactly on every surface of revolution.
|
//! rotated nodes then lie exactly on every surface of revolution.
|
||||||
@@ -31,7 +32,8 @@ use super::arrangement::{self, Arrangement, Q};
|
|||||||
use super::brep::{Brep, Face};
|
use super::brep::{Brep, Face};
|
||||||
use super::geom::{Inside, Surface, add, dist, scale, unit};
|
use super::geom::{Inside, Surface, add, dist, scale, unit};
|
||||||
use super::interval::Intervals;
|
use super::interval::Intervals;
|
||||||
use super::region::{self, Scheme};
|
use super::region::Scheme;
|
||||||
|
use super::section;
|
||||||
use super::{Ctx, Raw};
|
use super::{Ctx, Raw};
|
||||||
use crate::predicates::Sign;
|
use crate::predicates::Sign;
|
||||||
use crate::seeds::EdgeSeed;
|
use crate::seeds::EdgeSeed;
|
||||||
@@ -435,27 +437,7 @@ pub(crate) fn mesh(ctx: &Ctx, prepared: &Prepared, refine: u32) -> Result<Raw> {
|
|||||||
let goal = ideal_divisions(ctx.field, &curve3d(c))?;
|
let goal = ideal_divisions(ctx.field, &curve3d(c))?;
|
||||||
ia.var(goal * fine, 1);
|
ia.var(goal * fine, 1);
|
||||||
}
|
}
|
||||||
let oriented = |c: usize, fwd: bool| -> Vec<Q> {
|
let schemes = section::schemes(arr, used, &mut ia);
|
||||||
let mut v: Vec<Q> =
|
|
||||||
arr.curves.get(c).map(|x| x.points.iter().map(|&p| arr.points.get(p).copied().unwrap_or([0.0; 2])).collect()).unwrap_or_default();
|
|
||||||
if !fwd {
|
|
||||||
v.reverse();
|
|
||||||
}
|
|
||||||
v
|
|
||||||
};
|
|
||||||
let mut schemes: Vec<Scheme> = vec![Scheme::Free; arr.regions.len()];
|
|
||||||
for (r, region) in arr.regions.iter().enumerate() {
|
|
||||||
if !used.get(r).copied().unwrap_or(false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let polylines: Vec<Vec<Vec<Q>>> = region.loops.iter().map(|lp| lp.iter().map(|&(c, f)| oriented(c, f)).collect()).collect();
|
|
||||||
let scheme = region::classify(&polylines);
|
|
||||||
let vars: Vec<Vec<usize>> = region.loops.iter().map(|lp| lp.iter().map(|x| x.0).collect()).collect();
|
|
||||||
region::constrain(&mut ia, &vars, scheme);
|
|
||||||
if let Some(slot) = schemes.get_mut(r) {
|
|
||||||
*slot = scheme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let seeds = Seeds::of(ctx, frame, h_ref, intervals, setup.h_tol, &(0..arr.curves.len()).map(curve3d).collect::<Vec<_>>())?;
|
let seeds = Seeds::of(ctx, frame, h_ref, intervals, setup.h_tol, &(0..arr.curves.len()).map(curve3d).collect::<Vec<_>>())?;
|
||||||
for (c, seed) in seeds.curves.iter().enumerate() {
|
for (c, seed) in seeds.curves.iter().enumerate() {
|
||||||
if let Some((seed, _)) = seed {
|
if let Some((seed, _)) = seed {
|
||||||
@@ -483,85 +465,21 @@ pub(crate) fn mesh(ctx: &Ctx, prepared: &Prepared, refine: u32) -> Result<Raw> {
|
|||||||
}
|
}
|
||||||
layers.push(n);
|
layers.push(n);
|
||||||
}
|
}
|
||||||
// 2D nodes: curve ends, then curve interiors (projected onto analytic walls).
|
// The cross-section's nodes (curve interiors projected onto analytic walls) and quadrilaterals.
|
||||||
let mut nodes2: Vec<Q> = Vec::new();
|
let place = |c: usize, n: usize| -> Result<Vec<Q>> {
|
||||||
let mut point_node: HashMap<usize, u32> = HashMap::new();
|
|
||||||
let mut curve_nodes: Vec<Vec<u32>> = Vec::with_capacity(arr.curves.len());
|
|
||||||
for (c, curve) in arr.curves.iter().enumerate() {
|
|
||||||
let n = counts.get(c).copied().unwrap_or(1);
|
|
||||||
let pts3 = match seeds.curves.get(c).copied().flatten() {
|
let pts3 = match seeds.curves.get(c).copied().flatten() {
|
||||||
Some((seed, rev)) => crate::seeds::place(&curve3d(c), &seed.fractions(n, rev))?,
|
Some((seed, rev)) => crate::seeds::place(&curve3d(c), &seed.fractions(n, rev))?,
|
||||||
None => divide_polyline(ctx.field, &curve3d(c), n)?,
|
None => divide_polyline(ctx.field, &curve3d(c), n)?,
|
||||||
};
|
};
|
||||||
let wall = curve.tags.iter().find_map(|&f| brep.faces.get(f).and_then(|x| x.surface)).filter(|s| !matches!(s, Surface::Plane { .. }));
|
let wall = arr
|
||||||
let mut ids = Vec::with_capacity(pts3.len());
|
.curves
|
||||||
let last = pts3.len().saturating_sub(1);
|
.get(c)
|
||||||
for (k, p) in pts3.iter().enumerate() {
|
.and_then(|curve| curve.tags.iter().find_map(|&f| brep.faces.get(f).and_then(|x| x.surface)))
|
||||||
let end = if k == 0 {
|
.filter(|s| !matches!(s, Surface::Plane { .. }));
|
||||||
curve.points.first()
|
Ok(pts3.iter().map(|p| frame.coords(wall.and_then(|s| s.project(*p)).unwrap_or(*p)).0).collect())
|
||||||
} else if k == last {
|
};
|
||||||
curve.points.last()
|
let section::Section { nodes: nodes2, quads: region_quads, schemes } =
|
||||||
} else {
|
section::mesh(arr, used, schemes, &counts, &place, &plane_field, &ctx.opts.quad)?;
|
||||||
None
|
|
||||||
};
|
|
||||||
let id = match end {
|
|
||||||
Some(&pid) => *point_node.entry(pid).or_insert_with(|| {
|
|
||||||
nodes2.push(arr.points.get(pid).copied().unwrap_or([0.0; 2]));
|
|
||||||
nodes2.len() as u32 - 1
|
|
||||||
}),
|
|
||||||
None => {
|
|
||||||
let p = wall.and_then(|s| s.project(*p)).unwrap_or(*p);
|
|
||||||
nodes2.push(frame.coords(p).0);
|
|
||||||
nodes2.len() as u32 - 1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ids.push(id);
|
|
||||||
}
|
|
||||||
curve_nodes.push(ids);
|
|
||||||
}
|
|
||||||
// Quad meshes of the filled regions.
|
|
||||||
let mut region_quads: Vec<Vec<[u32; 4]>> = vec![Vec::new(); arr.regions.len()];
|
|
||||||
let quad_opts = &ctx.opts.quad;
|
|
||||||
for (r, region) in arr.regions.iter().enumerate() {
|
|
||||||
if !used.get(r).copied().unwrap_or(false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut loops_ids: Vec<Vec<u32>> = Vec::with_capacity(region.loops.len());
|
|
||||||
let mut starts: Vec<Vec<usize>> = Vec::new();
|
|
||||||
for lp in ®ion.loops {
|
|
||||||
let mut ids = Vec::new();
|
|
||||||
let mut st = Vec::new();
|
|
||||||
for &(c, fwd) in lp {
|
|
||||||
st.push(ids.len());
|
|
||||||
let cn = curve_nodes.get(c).map(Vec::as_slice).unwrap_or(&[]);
|
|
||||||
let mut seq: Vec<u32> = cn.to_vec();
|
|
||||||
if !fwd {
|
|
||||||
seq.reverse();
|
|
||||||
}
|
|
||||||
seq.pop();
|
|
||||||
ids.extend(seq);
|
|
||||||
}
|
|
||||||
loops_ids.push(ids);
|
|
||||||
starts.push(st);
|
|
||||||
}
|
|
||||||
let at = |i: u32| nodes2.get(i as usize).copied().unwrap_or([0.0; 2]);
|
|
||||||
let loops: Vec<Vec<Q>> = loops_ids.iter().map(|l| l.iter().map(|&i| at(i)).collect()).collect();
|
|
||||||
let scheme = schemes.get(r).copied().unwrap_or(Scheme::Free);
|
|
||||||
let (mesh, used_scheme) = region::mesh(&loops, &starts, scheme, &plane_field, quad_opts).map_err(|e| region_error(r, e))?;
|
|
||||||
if let Some(slot) = schemes.get_mut(r) {
|
|
||||||
*slot = used_scheme;
|
|
||||||
}
|
|
||||||
let boundary: Vec<u32> = loops_ids.concat();
|
|
||||||
let mut local: Vec<u32> = boundary.clone();
|
|
||||||
for p in mesh.nodes.iter().skip(mesh.boundary_nodes) {
|
|
||||||
nodes2.push(*p);
|
|
||||||
local.push(nodes2.len() as u32 - 1);
|
|
||||||
}
|
|
||||||
let quads = region_quads.get_mut(r).ok_or_else(|| Error::Failed("region index".into()))?;
|
|
||||||
for q in &mesh.quads {
|
|
||||||
quads.push(q.map(|i| local.get(i as usize).copied().unwrap_or(0)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Layer coordinates.
|
// Layer coordinates.
|
||||||
let mut heights: Vec<f64> = Vec::new();
|
let mut heights: Vec<f64> = Vec::new();
|
||||||
let mut first_layer: Vec<usize> = Vec::with_capacity(intervals.len());
|
let mut first_layer: Vec<usize> = Vec::with_capacity(intervals.len());
|
||||||
@@ -885,13 +803,6 @@ fn cuts(arr: &Arrangement, used: &[bool], tol: f64) -> Vec<(Vec<Q>, usize)> {
|
|||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
fn region_error(r: usize, e: Error) -> Error {
|
|
||||||
match e {
|
|
||||||
Error::Limit(m) => Error::Limit(m),
|
|
||||||
other => Error::Failed(format!("cross-section region {r}: {other}")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A point well inside a region: off the middle of one of its outer edges, inside the outer
|
/// A point well inside a region: off the middle of one of its outer edges, inside the outer
|
||||||
/// loop, outside the holes and clear of every boundary segment.
|
/// loop, outside the holes and clear of every boundary segment.
|
||||||
fn interior_point(arr: &Arrangement, region: &arrangement::Region) -> Result<Q> {
|
fn interior_point(arr: &Arrangement, region: &arrangement::Region) -> Result<Q> {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ mod interval;
|
|||||||
mod layered;
|
mod layered;
|
||||||
mod mapped;
|
mod mapped;
|
||||||
mod region;
|
mod region;
|
||||||
|
mod section;
|
||||||
mod sweep;
|
mod sweep;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -163,7 +163,22 @@ pub(crate) fn constrain(ia: &mut Intervals, loops: &[Vec<usize>], scheme: Scheme
|
|||||||
/// positions where its curve uses start. Returns the mesh (boundary nodes first, in the order of
|
/// positions where its curve uses start. Returns the mesh (boundary nodes first, in the order of
|
||||||
/// the loops) and the scheme used.
|
/// the loops) and the scheme used.
|
||||||
pub(crate) fn mesh(loops: &[Vec<Q>], starts: &[Vec<usize>], scheme: Scheme, size: &dyn SizeField, opts: &QuadOptions) -> Result<(QuadMesh, Scheme)> {
|
pub(crate) fn mesh(loops: &[Vec<Q>], starts: &[Vec<usize>], scheme: Scheme, size: &dyn SizeField, opts: &QuadOptions) -> Result<(QuadMesh, Scheme)> {
|
||||||
let structured = match scheme {
|
match mesh_structured(loops, starts, scheme, size, opts)? {
|
||||||
|
Some(m) => Ok((m, scheme)),
|
||||||
|
None => Ok((free_quad_mesh(loops, size, opts)?, Scheme::Free)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The structured mesh of a region with its scheme, or `None` when the scheme is free or its
|
||||||
|
/// grid would fold.
|
||||||
|
pub(crate) fn mesh_structured(
|
||||||
|
loops: &[Vec<Q>],
|
||||||
|
starts: &[Vec<usize>],
|
||||||
|
scheme: Scheme,
|
||||||
|
size: &dyn SizeField,
|
||||||
|
opts: &QuadOptions,
|
||||||
|
) -> Result<Option<QuadMesh>> {
|
||||||
|
Ok(match scheme {
|
||||||
Scheme::Mapped(c) => {
|
Scheme::Mapped(c) => {
|
||||||
let s0 = starts.first();
|
let s0 = starts.first();
|
||||||
let pos = c.map(|k| s0.and_then(|s| s.get(k)).copied().unwrap_or(0));
|
let pos = c.map(|k| s0.and_then(|s| s.get(k)).copied().unwrap_or(0));
|
||||||
@@ -181,11 +196,7 @@ pub(crate) fn mesh(loops: &[Vec<Q>], starts: &[Vec<usize>], scheme: Scheme, size
|
|||||||
Scheme::Annulus => annulus(loops, size, opts)?,
|
Scheme::Annulus => annulus(loops, size, opts)?,
|
||||||
Scheme::Disk => disk(loops, size, opts)?,
|
Scheme::Disk => disk(loops, size, opts)?,
|
||||||
Scheme::Free => None,
|
Scheme::Free => None,
|
||||||
};
|
})
|
||||||
match structured {
|
|
||||||
Some(m) => Ok((m, scheme)),
|
|
||||||
None => Ok((free_quad_mesh(loops, size, opts)?, Scheme::Free)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The triangle primitive on one loop with corners at node positions `corners` (in loop order),
|
/// The triangle primitive on one loop with corners at node positions `corners` (in loop order),
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
//! The quadrilateral mesh of a swept cross-section: the scheme of every filled region of the
|
||||||
|
//! arrangement and its interval constraints, then, on the assigned counts, the nodes of every
|
||||||
|
//! curve (shared by the regions on either side) and each region's quadrilaterals.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use super::arrangement::{Arrangement, Q};
|
||||||
|
use super::interval::Intervals;
|
||||||
|
use super::region::{self, Scheme};
|
||||||
|
use crate::quad::QuadOptions;
|
||||||
|
use crate::size::SizeField;
|
||||||
|
use crate::{Error, Result};
|
||||||
|
|
||||||
|
/// A meshed cross-section: its nodes and, per region of the arrangement, its quadrilaterals and
|
||||||
|
/// the scheme that made them (free for regions not filled).
|
||||||
|
pub(crate) struct Section {
|
||||||
|
pub nodes: Vec<Q>,
|
||||||
|
pub quads: Vec<Vec<[u32; 4]>>,
|
||||||
|
pub schemes: Vec<Scheme>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The scheme of each filled region (`used`), its constraints added to `ia` (one variable per
|
||||||
|
/// curve of the arrangement, by curve index).
|
||||||
|
pub(crate) fn schemes(arr: &Arrangement, used: &[bool], ia: &mut Intervals) -> Vec<Scheme> {
|
||||||
|
let oriented = |c: usize, fwd: bool| -> Vec<Q> {
|
||||||
|
let mut v: Vec<Q> =
|
||||||
|
arr.curves.get(c).map(|x| x.points.iter().map(|&p| arr.points.get(p).copied().unwrap_or([0.0; 2])).collect()).unwrap_or_default();
|
||||||
|
if !fwd {
|
||||||
|
v.reverse();
|
||||||
|
}
|
||||||
|
v
|
||||||
|
};
|
||||||
|
let mut schemes: Vec<Scheme> = vec![Scheme::Free; arr.regions.len()];
|
||||||
|
for (r, region) in arr.regions.iter().enumerate() {
|
||||||
|
if !used.get(r).copied().unwrap_or(false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let polylines: Vec<Vec<Vec<Q>>> = region.loops.iter().map(|lp| lp.iter().map(|&(c, f)| oriented(c, f)).collect()).collect();
|
||||||
|
let scheme = region::classify(&polylines);
|
||||||
|
let vars: Vec<Vec<usize>> = region.loops.iter().map(|lp| lp.iter().map(|x| x.0).collect()).collect();
|
||||||
|
region::constrain(ia, &vars, scheme);
|
||||||
|
if let Some(slot) = schemes.get_mut(r) {
|
||||||
|
*slot = scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
schemes
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mesh the filled regions on the curve counts `counts`: `place(c, n)` gives the `n + 1` points
|
||||||
|
/// of curve `c` (its interior ones are used; its ends are the arrangement's points, shared).
|
||||||
|
/// The schemes come from [`schemes`]; a structured one whose grid would fold is meshed free.
|
||||||
|
pub(crate) fn mesh(
|
||||||
|
arr: &Arrangement,
|
||||||
|
used: &[bool],
|
||||||
|
mut schemes: Vec<Scheme>,
|
||||||
|
counts: &[usize],
|
||||||
|
place: &dyn Fn(usize, usize) -> Result<Vec<Q>>,
|
||||||
|
field: &dyn SizeField,
|
||||||
|
opts: &QuadOptions,
|
||||||
|
) -> Result<Section> {
|
||||||
|
// Nodes: curve ends, then curve interiors.
|
||||||
|
let mut nodes: Vec<Q> = Vec::new();
|
||||||
|
let mut point_node: HashMap<usize, u32> = HashMap::new();
|
||||||
|
let mut curve_nodes: Vec<Vec<u32>> = Vec::with_capacity(arr.curves.len());
|
||||||
|
for (c, curve) in arr.curves.iter().enumerate() {
|
||||||
|
let n = counts.get(c).copied().unwrap_or(1);
|
||||||
|
let pts = place(c, n)?;
|
||||||
|
let mut ids = Vec::with_capacity(pts.len());
|
||||||
|
let last = pts.len().saturating_sub(1);
|
||||||
|
for (k, p) in pts.iter().enumerate() {
|
||||||
|
let end = if k == 0 {
|
||||||
|
curve.points.first()
|
||||||
|
} else if k == last {
|
||||||
|
curve.points.last()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let id = match end {
|
||||||
|
Some(&pid) => *point_node.entry(pid).or_insert_with(|| {
|
||||||
|
nodes.push(arr.points.get(pid).copied().unwrap_or([0.0; 2]));
|
||||||
|
nodes.len() as u32 - 1
|
||||||
|
}),
|
||||||
|
None => {
|
||||||
|
nodes.push(*p);
|
||||||
|
nodes.len() as u32 - 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ids.push(id);
|
||||||
|
}
|
||||||
|
curve_nodes.push(ids);
|
||||||
|
}
|
||||||
|
let mut region_quads: Vec<Vec<[u32; 4]>> = vec![Vec::new(); arr.regions.len()];
|
||||||
|
for (r, region) in arr.regions.iter().enumerate() {
|
||||||
|
if !used.get(r).copied().unwrap_or(false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut loops_ids: Vec<Vec<u32>> = Vec::with_capacity(region.loops.len());
|
||||||
|
let mut starts: Vec<Vec<usize>> = Vec::new();
|
||||||
|
for lp in ®ion.loops {
|
||||||
|
let mut ids = Vec::new();
|
||||||
|
let mut st = Vec::new();
|
||||||
|
for &(c, fwd) in lp {
|
||||||
|
st.push(ids.len());
|
||||||
|
let mut seq: Vec<u32> = curve_nodes.get(c).cloned().unwrap_or_default();
|
||||||
|
if !fwd {
|
||||||
|
seq.reverse();
|
||||||
|
}
|
||||||
|
seq.pop();
|
||||||
|
ids.extend(seq);
|
||||||
|
}
|
||||||
|
loops_ids.push(ids);
|
||||||
|
starts.push(st);
|
||||||
|
}
|
||||||
|
let at = |i: u32| nodes.get(i as usize).copied().unwrap_or([0.0; 2]);
|
||||||
|
let loops: Vec<Vec<Q>> = loops_ids.iter().map(|l| l.iter().map(|&i| at(i)).collect()).collect();
|
||||||
|
let scheme = schemes.get(r).copied().unwrap_or(Scheme::Free);
|
||||||
|
let (mesh, used_scheme) = region::mesh(&loops, &starts, scheme, field, opts).map_err(|e| region_error(r, e))?;
|
||||||
|
if let Some(slot) = schemes.get_mut(r) {
|
||||||
|
*slot = used_scheme;
|
||||||
|
}
|
||||||
|
let mut local: Vec<u32> = loops_ids.concat();
|
||||||
|
for p in mesh.nodes.iter().skip(mesh.boundary_nodes) {
|
||||||
|
nodes.push(*p);
|
||||||
|
local.push(nodes.len() as u32 - 1);
|
||||||
|
}
|
||||||
|
let quads = region_quads.get_mut(r).ok_or_else(|| Error::Failed("region index".into()))?;
|
||||||
|
for q in &mesh.quads {
|
||||||
|
quads.push(q.map(|i| local.get(i as usize).copied().unwrap_or(0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Section { nodes, quads: region_quads, schemes })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn region_error(r: usize, e: Error) -> Error {
|
||||||
|
match e {
|
||||||
|
Error::Limit(m) => Error::Limit(m),
|
||||||
|
other => Error::Failed(format!("cross-section region {r}: {other}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user