mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-09 18:01:35 -06:00
address review: contour depth counts containers, glTF wrap copies and FBX textures share the import pixel budget
This commit is contained in:
+14
-17
@@ -130,24 +130,21 @@ pub fn group_contours(contours: Vec<Vec<P>>) -> Vec<Outline> {
|
||||
.iter()
|
||||
.map(|c| c.iter().fold([[f64::MAX; 2], [f64::MIN; 2]], |[lo, hi], p| [[lo[0].min(p[0]), lo[1].min(p[1])], [hi[0].max(p[0]), hi[1].max(p[1])]]))
|
||||
.collect();
|
||||
// The innermost container of each contour: the smallest larger contour holding a vertex of
|
||||
// it (nested contours do not cross); its bounding box must hold the vertex first.
|
||||
let parent: Vec<Option<usize>> = (0..n)
|
||||
.map(|i| {
|
||||
let p = cs[i][0];
|
||||
(0..n)
|
||||
.filter(|&j| j != i && area[j] > area[i])
|
||||
.filter(|&j| (bounds[j][0][0]..=bounds[j][1][0]).contains(&p[0]) && (bounds[j][0][1]..=bounds[j][1][1]).contains(&p[1]))
|
||||
.filter(|&j| point_in(p, &cs[j]))
|
||||
.min_by(|&a, &b| area[a].total_cmp(&area[b]))
|
||||
})
|
||||
.collect();
|
||||
// Nesting depth, containers (the larger contours) first.
|
||||
let mut by_area: Vec<usize> = (0..n).collect();
|
||||
by_area.sort_by(|&a, &b| area[b].total_cmp(&area[a]));
|
||||
// The containers of each contour: the larger contours holding a vertex of it (nested contours
|
||||
// do not cross); the bounding box must hold the vertex first. Depth is their count, so
|
||||
// overlapping (non-nested) paths keep every container they sit inside; the innermost
|
||||
// container (smallest area) takes a hole.
|
||||
let mut parent: Vec<Option<usize>> = vec![None; n];
|
||||
let mut depth = vec![0usize; n];
|
||||
for &i in &by_area {
|
||||
depth[i] = parent[i].map_or(0, |p| depth[p] + 1);
|
||||
for i in 0..n {
|
||||
let p = cs[i][0];
|
||||
let containers: Vec<usize> = (0..n)
|
||||
.filter(|&j| j != i && area[j] > area[i])
|
||||
.filter(|&j| (bounds[j][0][0]..=bounds[j][1][0]).contains(&p[0]) && (bounds[j][0][1]..=bounds[j][1][1]).contains(&p[1]))
|
||||
.filter(|&j| point_in(p, &cs[j]))
|
||||
.collect();
|
||||
depth[i] = containers.len();
|
||||
parent[i] = containers.into_iter().min_by(|&a, &b| area[a].total_cmp(&area[b]));
|
||||
}
|
||||
let mut outlines: Vec<Outline> = vec![];
|
||||
let mut slot = vec![usize::MAX; n];
|
||||
|
||||
@@ -851,6 +851,8 @@ fn parse_within(bytes: &[u8], resolve: &dyn Fn(&str) -> Option<Vec<u8>>, limit:
|
||||
let mut textures: Vec<Arc<Texture>> = vec![];
|
||||
let mut material_of: HashMap<i64, usize> = HashMap::new();
|
||||
let mut texture_of: HashMap<i64, Option<usize>> = HashMap::new();
|
||||
// One pixel budget for every texture of the import.
|
||||
let mut pixels_left = crate::MAX_IMPORT_TEXTURE_PIXELS;
|
||||
let mut load_texture = |tid: i64, textures: &mut Vec<Arc<Texture>>| -> Option<usize> {
|
||||
if let Some(t) = texture_of.get(&tid) {
|
||||
return *t;
|
||||
@@ -874,7 +876,7 @@ fn parse_within(bytes: &[u8], resolve: &dyn Fn(&str) -> Option<Vec<u8>>, limit:
|
||||
resolve(&f).or_else(|| resolve(f.rsplit('/').next()?))
|
||||
})
|
||||
})?;
|
||||
let t = Texture::decode(&bytes).ok()?;
|
||||
let t = Texture::decode_within(&bytes, &mut pixels_left).ok()?;
|
||||
textures.push(Arc::new(t));
|
||||
Some(textures.len() - 1)
|
||||
})();
|
||||
|
||||
@@ -400,10 +400,27 @@ pub(crate) fn parse_with_budget(bytes: &[u8], resolve: &dyn Fn(&str) -> Option<V
|
||||
let ws = samp.and_then(|s| s.get("wrapS")).and_then(Value::as_u64).unwrap_or(10497);
|
||||
let wt = samp.and_then(|s| s.get("wrapT")).and_then(Value::as_u64).unwrap_or(10497);
|
||||
let w = [wrap(ws), wrap(wt)];
|
||||
let i = *texture_of.entry((src, w)).or_insert_with(|| {
|
||||
textures.push(if img.wrap == w { img } else { Arc::new(Texture { wrap: w, ..(*img).clone() }) });
|
||||
textures.len() - 1
|
||||
});
|
||||
let i = match texture_of.get(&(src, w)) {
|
||||
Some(&i) => i,
|
||||
None => {
|
||||
let tex = if img.wrap == w {
|
||||
img
|
||||
} else {
|
||||
// A wrap variant copies the pixels, so it is charged to the import budget too;
|
||||
// one that does not fit is left out like an undecodable image.
|
||||
let cost = u64::from(img.width) * u64::from(img.height);
|
||||
if cost > pixels_left {
|
||||
tex_map.push(None);
|
||||
continue;
|
||||
}
|
||||
pixels_left -= cost;
|
||||
Arc::new(Texture { wrap: w, ..(*img).clone() })
|
||||
};
|
||||
textures.push(tex);
|
||||
texture_of.insert((src, w), textures.len() - 1);
|
||||
textures.len() - 1
|
||||
}
|
||||
};
|
||||
tex_map.push(Some(i));
|
||||
}
|
||||
let tref = |v: Option<&Value>| -> Option<TexRef> {
|
||||
|
||||
@@ -432,6 +432,16 @@ fn contours_group_into_outlines_by_nesting() {
|
||||
assert_eq!(g.iter().map(|o| o.holes.len()).sum::<usize>(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlapping_contours_keep_every_container_in_their_depth() {
|
||||
let rect = |x0: f64, y0: f64, x1: f64, y1: f64| vec![[x0, y0], [x1, y0], [x1, y1], [x0, y1]];
|
||||
// C's first vertex lies inside both A and B, which overlap without nesting: C is two deep,
|
||||
// an outer contour, not a hole cut out of B.
|
||||
let g = group_contours(vec![rect(0.0, 0.0, 10.0, 10.0), rect(5.0, -2.0, 13.0, 8.0), rect(9.0, 1.0, 12.0, 2.0)]);
|
||||
assert_eq!(g.len(), 3);
|
||||
assert_eq!(g.iter().map(|o| o.holes.len()).sum::<usize>(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gltf_cameras_and_punctual_lights() {
|
||||
let doc = r#"{
|
||||
|
||||
Reference in New Issue
Block a user