Naming: a blend's seams split its edge's name at the | between its two faces

A blend face's name holds the edge it replaced, <face>|<face>, and a side
of that edge can be a blend face whose own name holds a |. Whether a face
is a contact of the blend was read as the edge's name starting or ending
with the face's name next to a |, so the r2 round's own face F2:side read
as a contact of the r1 round F4:blend:F1:+y|F3:blend:F1:+z|F2:side. The
edge's name is now split into its two faces, a blend side read whole as
the two faces of its own edge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jyviko
2026-10-31 01:34:06 -04:00
committed by jyviko
parent 09bc14dbd8
commit 469a3b2f9d
+39 -5
View File
@@ -340,11 +340,7 @@ pub fn blend_seams(b: &ModelBody) -> Vec<BlendSeam> {
all(j).into_iter().any(|name| {
let (core, instance) = split_instance(name);
let face = crate::piece_names::strip_qualifiers(core);
edges.iter().any(|(edge, at)| {
at == instance
&& (edge.strip_prefix(face.as_str()).is_some_and(|r| r.starts_with('|'))
|| edge.strip_suffix(face.as_str()).is_some_and(|r| r.ends_with('|')))
})
edges.iter().any(|(edge, at)| at == instance && edge_sides(edge).is_some_and(|(a, b)| a == face || b == face))
})
};
let mut out = Vec::new();
@@ -363,6 +359,44 @@ pub fn blend_seams(b: &ModelBody) -> Vec<BlendSeam> {
out
}
/// The two faces an edge name `<face A>|<face B>` joins. A blend face's own name holds the edge it
/// replaced (`F<feature>:blend:<face>|<face>`), so a side's name can hold `|` too: the split is the
/// one `|` outside every blend face's edge (`F1:+y` and `F3:blend:F1:+z|F2:side` for
/// `F1:+y|F3:blend:F1:+z|F2:side`). `None` for a name that is not two faces.
fn edge_sides(edge: &str) -> Option<(&str, &str)> {
let first = face_name_end(edge, 0)?;
if edge.as_bytes().get(first) != Some(&b'|') || face_name_end(edge, first + 1)? != edge.len() {
return None;
}
Some((edge.get(..first)?, edge.get(first + 1..)?))
}
/// Where the face name starting at byte `from` of `s` ends: at the next `|`, or for a blend face
/// past the two faces of the edge its name holds (and theirs, for blends of blends).
fn face_name_end(s: &str, from: usize) -> Option<usize> {
const MARK: &str = ":blend:";
// The faces still to read: a blend face's name is read as the two of its edge.
let (mut pending, mut at) = (1usize, from);
loop {
let rest = s.get(at..)?;
let bar = rest.find('|').unwrap_or(rest.len());
if let Some(m) = rest.get(..bar)?.find(MARK) {
pending += 1;
at += m + MARK.len();
continue;
}
pending -= 1;
at += bar;
if pending == 0 {
return Some(at);
}
if s.as_bytes().get(at) != Some(&b'|') {
return None;
}
at += 1;
}
}
/// A name and the copy it names (`@F<feature>.<instance>`, a pattern's or a paste's copy of a
/// tool face; empty for the original).
fn split_instance(name: &str) -> (&str, &str) {