mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 07:13:46 +00:00
.ai import: a warp of type's second try leaves checks for the envelopes after it
A warp of type may now spend up to four checks (its warp and its saved grid, each with and without strokes). A try after the first is only made while more checks are left than there are envelopes of type and warped text objects still to come, so one envelope's retries don't use up the checks of the others. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
198bb6dffd
commit
d0c99cba4b
@@ -529,8 +529,11 @@ impl WarpedType {
|
||||
}
|
||||
let mut shown = BTreeSet::new();
|
||||
doc.layers.iter().for_each(|l| collect_shown(l, &mut shown));
|
||||
for envelope in std::mem::take(&mut self.envelopes) {
|
||||
match self.typed_live(doc, envelope, &type_of, &shown) {
|
||||
let envelopes = std::mem::take(&mut self.envelopes);
|
||||
let mut waiting = envelopes.len();
|
||||
for envelope in envelopes {
|
||||
waiting -= 1;
|
||||
match self.typed_live(doc, envelope, &type_of, &shown, waiting) {
|
||||
Ok(put_back) => {
|
||||
settled.live += 1;
|
||||
settled.put_back.push(put_back);
|
||||
@@ -541,14 +544,16 @@ impl WarpedType {
|
||||
settled
|
||||
}
|
||||
|
||||
/// [`Self::settle_envelopes`] for `envelope`, the shown objects of `doc` being `shown`: the live
|
||||
/// envelope's id and what takes its place again, or why its drawn art stays.
|
||||
/// [`Self::settle_envelopes`] for `envelope`, the shown objects of `doc` being `shown` and
|
||||
/// `waiting` envelopes of type still to come: the live envelope's id and what takes its place
|
||||
/// again, or why its drawn art stays.
|
||||
fn typed_live(
|
||||
&mut self,
|
||||
doc: &mut Document,
|
||||
envelope: TypedEnvelope,
|
||||
type_of: &impl Fn(&Document, NodeId) -> Option<(NodeId, Node)>,
|
||||
shown: &BTreeSet<NodeId>,
|
||||
waiting: usize,
|
||||
) -> Result<(NodeId, Vec<Node>), TypeKept> {
|
||||
let TypedEnvelope { obj, mut before, mut inner, drawn } = envelope;
|
||||
let art = doc.node(drawn).cloned().ok_or(TypeKept::Read)?;
|
||||
@@ -595,8 +600,11 @@ impl WarpedType {
|
||||
(plain.visible, plain.opacity) = (true, 1.0);
|
||||
let page = self.page.filter(|_| shown.contains(&drawn));
|
||||
let mut alike = None;
|
||||
for kind in kinds {
|
||||
if self.checks == 0 {
|
||||
for (i, kind) in kinds.into_iter().enumerate() {
|
||||
// A second try is only made while more checks are left than there are envelopes of
|
||||
// type and warped text objects still to come (some of which may need none): one
|
||||
// envelope's tries don't use up the checks of the others.
|
||||
if self.checks == 0 || (i > 0 && self.checks <= waiting + self.looks.len()) {
|
||||
return Err(TypeKept::Budget);
|
||||
}
|
||||
self.checks -= 1;
|
||||
|
||||
@@ -5826,6 +5826,82 @@ fn a_warp_of_type_without_a_saved_grid_keeps_its_drawn_art() {
|
||||
assert_eq!(super::tests::program_warnings(&r.warnings), [crate::import::ai::ENVELOPE_TYPE_DRAWN], "{:?}", r.warnings);
|
||||
}
|
||||
|
||||
/// A warp of type tried again on its saved grid leaves a check for each envelope of type still to
|
||||
/// come: with one check too few for both, the next envelope, whose first try opens it, gets it.
|
||||
#[test]
|
||||
fn a_warp_of_type_tried_again_leaves_the_next_envelope_its_check() {
|
||||
use vectorcraft_doc::live::EnvelopeKind;
|
||||
let Some(family) = installed_family() else { return };
|
||||
let story = ("FLUX\r", 0, (8170.0, 8180.0), (0.0, 0.0), 0.0);
|
||||
let mut doc = text_document(&[story, story]).replace("(Helvetica)", &format!("({family})"));
|
||||
// [`native_with_text`] writes 60 characters a line: the end of the data, `~>`, isn't split.
|
||||
while ascii85(doc.as_bytes()).len() % 60 == 1 {
|
||||
doc.push('\n');
|
||||
}
|
||||
let text = made_envelope_type(&doc);
|
||||
// The first envelope, of story 0, draws as its saved grid and not as its warp: two checks.
|
||||
let mut shown = text.clone();
|
||||
shown.visible = true;
|
||||
let options = vectorcraft_doc::live::EnvelopeOptions::NEW;
|
||||
let shaped = Node::new(
|
||||
vectorcraft_doc::NodeId(1_000_000),
|
||||
NodeKind::Envelope {
|
||||
content: vec![std::sync::Arc::new(shown)],
|
||||
kind: Box::new(rising_kind()),
|
||||
fidelity: 50.0,
|
||||
editing: false,
|
||||
options,
|
||||
frame: Box::default(),
|
||||
},
|
||||
);
|
||||
let (drawn_grid, page_grid) = drawn_envelope(&shaped);
|
||||
let grid = type_envelope_on(&arc_keys(0.3), &rising_grid(), &drawn_grid, "");
|
||||
// The second, of story 1, draws as its warp: one check.
|
||||
let (drawn_arc, page_arc) = drawn_envelope(&arc_envelope(&text, 30.0));
|
||||
let arc = type_envelope(&arc_keys(0.3), &drawn_arc).replace("%_0 /StoryIndex", "%_1 /StoryIndex");
|
||||
// Envelopes of art before them use all but `left` of the import's checks.
|
||||
let hidden = |s: &str| -> String { s.lines().map(|l| format!("%_{l}\n")).collect() };
|
||||
let rect = "20 5 m\n20 95 L\n80 95 L\n80 5 L\n20 5 L\n";
|
||||
let corner = |x: f64, y: f64, nx: f64, ny: f64, px: f64, py: f64| format!("[0.8 1 {x} {y} {nx} {ny} 1 {px} {py} 1] /N X#\n");
|
||||
let mesh = format!(
|
||||
"/Mesh X!\n2 /Version X#\n/Cartesian /Type X#\n[1 1] /Size X#\n/Data X#\n/DeviceGray /CS X#\n[0 0] /P X#\n{}{}{}{}/E X#\n/End X!\n",
|
||||
corner(20.0, 5.0, 20.0, 35.0, 40.0, 5.0),
|
||||
corner(20.0, 95.0, 40.0, 95.0, 20.0, 65.0),
|
||||
corner(80.0, 95.0, 80.0, 65.0, 60.0, 95.0),
|
||||
corner(80.0, 5.0, 60.0, 5.0, 80.0, 35.0),
|
||||
);
|
||||
let red = "0 1 1 0 1 0 0 Xa\n";
|
||||
let filler = format!(
|
||||
"{}%_/ArtDictionary :\n%_131073 /Int (AIDeformOptionsKey) ,\n%_50 /Int (AIDeformFidelityKey) ,\n%_0 /Bool (AIDeformEditModeKey) ,\n%_X=\n{}%_X+\n%_ (AIDeformArtKey) ,\n%_2 /Int (AIDeformStyle) ,\n%_;\n%_\nu\n{red}{rect}f\nU\n(Adobe Deform Plugin) 1 0 0 XP\n",
|
||||
hidden(&format!("u\n{mesh}U\n")),
|
||||
hidden(&format!("u\n{red}{rect}f\nU\n")),
|
||||
);
|
||||
let page =
|
||||
format!("{}\n1 0 0 setrgbcolor 20 5 moveto 20 95 lineto 80 95 lineto 80 5 lineto closepath fill\n{page_grid}{page_arc}", page_square(10, 10));
|
||||
let file = |left: usize| {
|
||||
let body = format!("{}\n{}{grid}{arc}", square(10, 10), filler.repeat(crate::import::ai::MAX_CHECKED - left));
|
||||
eps(&page, &native_with_text(&layer("Art", true, &body), &doc))
|
||||
};
|
||||
// The live envelopes of type, by kind.
|
||||
let typed = |d: &Document| {
|
||||
let mut out = vec![];
|
||||
d.walk(|n| {
|
||||
if let NodeKind::Envelope { content, kind, .. } = &n.kind
|
||||
&& content.iter().any(|c| matches!(c.kind, NodeKind::Text(_)))
|
||||
{
|
||||
out.push(if matches!(**kind, EnvelopeKind::Mesh { .. }) { "mesh" } else { "warp" });
|
||||
}
|
||||
});
|
||||
out
|
||||
};
|
||||
let r = import(&file(3)).unwrap();
|
||||
assert_eq!(names(&r.document), ["Art"], "{:?}", r.warnings);
|
||||
assert_eq!(typed(&r.document), ["mesh", "warp"], "{:?}", r.warnings);
|
||||
let r = import(&file(2)).unwrap();
|
||||
assert_eq!(names(&r.document), ["Art"], "{:?}", r.warnings);
|
||||
assert_eq!(typed(&r.document), ["warp"], "{:?}", r.warnings);
|
||||
}
|
||||
|
||||
/// The fill of the first run of type `n`.
|
||||
fn text_fill(n: &Node) -> Option<vectorcraft_color::Color> {
|
||||
let NodeKind::Text(t) = &n.kind else { return None };
|
||||
|
||||
Reference in New Issue
Block a user