mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-14 13:36:53 +00:00
Place: DCS transparency interactions look only at what clipping paths show
Show DCS Transparency Interactions compared full object bounds, so a transparent object over the part of a DCS image that its clipping path hides marked the image. Bounds are now cut to the clipping paths of the clip groups and clipping layers that hold each object.
This commit is contained in:
@@ -1939,15 +1939,21 @@ fn status_rank(status: &str) -> usize {
|
||||
/// holds them) or overlap an object that is, as Show DCS Transparency Interactions marks them.
|
||||
fn dcs_transparency(d: &Document) -> BTreeSet<NodeId> {
|
||||
/// Each visible DCS image's id, bounds and whether it is transparent, and the bounds of each
|
||||
/// other visible object with its own transparency.
|
||||
fn walk(d: &Document, n: &Node, held: bool, images: &mut Vec<(NodeId, Rect, bool)>, see_through: &mut Vec<Rect>) {
|
||||
/// other visible object with its own transparency; bounds are cut to `clip`, the bounds of the
|
||||
/// clipping paths of the clip groups and layers holding the object.
|
||||
fn walk(d: &Document, n: &Node, held: bool, clip: Option<Rect>, images: &mut Vec<(NodeId, Rect, bool)>, see_through: &mut Vec<Rect>) {
|
||||
if !n.visible {
|
||||
return;
|
||||
}
|
||||
let own = n.shows_own_transparency();
|
||||
// The part of the object that shows through `clip`; none when it is clipped away.
|
||||
let shown = n.geometric_bounds().and_then(|b| match clip {
|
||||
Some(c) => Some(c.intersect(b)).filter(|i| i.area() > 0.0),
|
||||
None => Some(b),
|
||||
});
|
||||
match &n.kind {
|
||||
NodeKind::Image(im) if d.images.get(&im.key).is_some_and(ImageBlob::is_separated) => {
|
||||
if let Some(b) = n.geometric_bounds() {
|
||||
if let Some(b) = shown {
|
||||
images.push((n.id, b, held || own));
|
||||
// A transparent DCS image is an object with its own transparency for the
|
||||
// others it overlaps.
|
||||
@@ -1956,16 +1962,25 @@ fn dcs_transparency(d: &Document) -> BTreeSet<NodeId> {
|
||||
}
|
||||
}
|
||||
}
|
||||
_ if own => see_through.extend(n.geometric_bounds()),
|
||||
_ if own => see_through.extend(shown),
|
||||
_ => {}
|
||||
}
|
||||
for c in n.children().into_iter().flatten() {
|
||||
walk(d, c, held || own, images, see_through);
|
||||
let Some(children) = n.children() else { return };
|
||||
// A clip group's or clipping layer's first child clips the others.
|
||||
let clip = match &n.kind {
|
||||
NodeKind::Group { clip: true, .. } | NodeKind::Layer { clip: true, .. } => match children.first().and_then(|c| c.geometric_bounds()) {
|
||||
Some(c) => Some(clip.map_or(c, |o| o.intersect(c))),
|
||||
None => clip,
|
||||
},
|
||||
_ => clip,
|
||||
};
|
||||
for c in children {
|
||||
walk(d, c, held || own, clip, images, see_through);
|
||||
}
|
||||
}
|
||||
let (mut images, mut see_through) = (vec![], vec![]);
|
||||
for l in &d.layers {
|
||||
walk(d, l, false, &mut images, &mut see_through);
|
||||
walk(d, l, false, None, &mut images, &mut see_through);
|
||||
}
|
||||
images
|
||||
.into_iter()
|
||||
|
||||
@@ -657,6 +657,32 @@ fn dcs_transparency_marks_transparent_and_overlapped_dcs_images() {
|
||||
assert!(error(&mut s, "links.list", json!({ "dcsTransparency": "yes" })).contains("dcsTransparency"), "a flag that isn't true or false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dcs_transparency_looks_only_at_the_part_of_an_image_its_clipping_path_shows() {
|
||||
let dir = temp_dir("transparency-clipped");
|
||||
let path = write(&dir, "pic.eps", &two_plates());
|
||||
let mut s = session();
|
||||
let r = s.execute("file.place", &json!({ "path": path, "at": [100, 100] })).unwrap();
|
||||
let group = NodeId(r["ids"][0].as_u64().unwrap());
|
||||
let image = image_of(&s, &r["ids"][0]).id;
|
||||
let (clip, all) = {
|
||||
let doc = &s.doc().unwrap().doc;
|
||||
let NodeKind::Group { children, clip: true } = &doc.node(group).unwrap().kind else { panic!("a clipping group") };
|
||||
(children[0].geometric_bounds().unwrap(), doc.node(image).unwrap().geometric_bounds().unwrap())
|
||||
};
|
||||
assert!(all.x1 - clip.x1 > 2.0, "the file's clipping path shows the left half: {clip:?} of {all:?}");
|
||||
// A half-transparent square over the part of the image the clipping path hides.
|
||||
let x = (clip.x1 + all.x1) / 2.0 - 1.0;
|
||||
let over = s.execute("shape.rectangle", &json!({"x": x, "y": all.y0, "width": 2, "height": 2})).unwrap()["id"].as_u64().unwrap();
|
||||
s.execute("transparency.set", &json!({"ids": [over], "opacity": 50})).unwrap();
|
||||
let marked = |s: &mut Session| s.execute("links.list", &json!({ "dcsTransparency": true })).unwrap()["links"][0]["dcsTransparency"] == true;
|
||||
assert!(!marked(&mut s), "the square is over the hidden part only");
|
||||
s.execute("transparency.set", &json!({"ids": [over], "opacity": 100})).unwrap();
|
||||
let shown = s.execute("shape.rectangle", &json!({"x": clip.x0, "y": all.y0, "width": 2, "height": 2})).unwrap()["id"].as_u64().unwrap();
|
||||
s.execute("transparency.set", &json!({"ids": [shown], "opacity": 50})).unwrap();
|
||||
assert!(marked(&mut s), "a square over the part that shows");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dcs_transparency_counts_a_transparent_dcs_image_over_another() {
|
||||
let dir = temp_dir("transparency-two");
|
||||
|
||||
+2
-1
@@ -6705,7 +6705,8 @@ DCS Transparency Interactions, off by default) adds `dcsTransparency: true` to t
|
||||
and the panel shows a warning mark on them. VectorCraft's rule for it: the image, or a group or layer holding it, has
|
||||
an opacity below 100%, a blend mode other than Normal, an opacity mask or an effect, or its bounds overlap the
|
||||
bounds of another visible object that has one of those or a gradient with a transparent stop (in a fill, a stroke or
|
||||
a character's fill or stroke); a DCS image with transparency of its own counts as such an object. The check looks at
|
||||
a character's fill or stroke); a DCS image with transparency of its own counts as such an object. Bounds are cut to the
|
||||
bounds of the clipping paths of the clip groups and clipping layers holding an object. The check looks at
|
||||
every object in the layers each time the list is read. A `dcsTransparency` that is not true or false is an error.
|
||||
`links.goTo {id}` selects an image; `links.embed {ids?}` keeps linked files' pixels in the document (an image whose
|
||||
file is missing stays linked: relink it first); `links.unembed {id, path}` writes an embedded image to a file
|
||||
|
||||
Reference in New Issue
Block a user