mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 02:39:42 +00:00
Artboards: Fit to Artwork Bounds' kept answer kept by the document, with the plug-ins and fonts it reads
The answer whether Fit to Artwork Bounds has art to fit moves from a function-local thread_local to DocState, beside any_empty_artboards, and drops with the document. It is kept for that version of the document (held weakly, as any_empty_artboards holds its own) and the artboards asked about, and measured again when the installed plug-ins, whose effects draw art, or the installed fonts, which set type drawn by its effects' art, change: their generations are an explicit input of the measure. A change of the selection or the view keeps it. The test counts the measures, as the Delete Empty Artboards check's does: a later frame, select.set and a view change keep the answer; an edit, other artboards, and each generation measure again. Taking any part out of the key fails it.
This commit is contained in:
@@ -112,6 +112,8 @@ pub struct DocState {
|
||||
pub opened_by: Option<Arc<file_access::AutomationRoots>>,
|
||||
/// What [`DocState::any_empty_artboards`] last measured. Not saved.
|
||||
empty_artboards: std::cell::RefCell<EmptyArtboards>,
|
||||
/// What [`DocState::any_art_to_fit`] last measured. Not saved.
|
||||
art_to_fit: std::cell::RefCell<Option<ArtToFit>>,
|
||||
}
|
||||
|
||||
/// Whether a document has an artboard for Delete Empty Artboards to delete, with the document that
|
||||
@@ -124,10 +126,23 @@ struct EmptyArtboards {
|
||||
any: bool,
|
||||
}
|
||||
|
||||
/// Whether an unlocked artboard of those asked about has art that Fit to Artwork Bounds fits it
|
||||
/// to, with what was measured: the document (held as [`EmptyArtboards::doc`] holds it), the
|
||||
/// artboards, and the generations of what the measure reads outside the document.
|
||||
#[derive(Clone, Debug)]
|
||||
struct ArtToFit {
|
||||
doc: std::sync::Weak<Document>,
|
||||
indices: Vec<usize>,
|
||||
outside: (u64, u64),
|
||||
any: bool,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
thread_local! {
|
||||
/// Tests: how many times [`DocState::any_empty_artboards`] measured a document on this thread.
|
||||
pub(crate) static EMPTY_ARTBOARD_MEASURES: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
|
||||
/// Tests: how many times [`DocState::any_art_to_fit`] measured a document on this thread.
|
||||
pub(crate) static ART_TO_FIT_MEASURES: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
|
||||
}
|
||||
|
||||
static NEXT_DOC_UID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
|
||||
@@ -309,6 +324,7 @@ impl DocState {
|
||||
last_perspective: None,
|
||||
opened_by: file_access::current(),
|
||||
empty_artboards: Default::default(),
|
||||
art_to_fit: Default::default(),
|
||||
}
|
||||
}
|
||||
/// `measure`'s answer for the document now. `measure` runs once for each version of the document,
|
||||
@@ -323,6 +339,33 @@ impl DocState {
|
||||
}
|
||||
last.any
|
||||
}
|
||||
/// Whether an unlocked artboard of `indices` has art that Fit to Artwork Bounds fits it to. A
|
||||
/// menu asks on every frame it is open, and measuring builds the solids of 3D effects, so the
|
||||
/// answer is kept for this version of the document and those artboards, and a change of the
|
||||
/// selection or the view keeps it. It is measured again when what the measure reads outside the
|
||||
/// document changes: the installed plug-ins, whose effects draw art, and the installed fonts,
|
||||
/// which set type drawn by its effects' art (an envelope, a 3D effect).
|
||||
pub fn any_art_to_fit(&self, indices: &[usize]) -> bool {
|
||||
self.any_art_to_fit_with(indices, (vectorcraft_plugins::registry::revision(), vectorcraft_text::FontDb::global().generation()))
|
||||
}
|
||||
/// [`Self::any_art_to_fit`], with `outside` the generations of the plug-ins and the fonts.
|
||||
pub(crate) fn any_art_to_fit_with(&self, indices: &[usize], outside: (u64, u64)) -> bool {
|
||||
let mut last = self.art_to_fit.borrow_mut();
|
||||
if let Some(k) = last.as_ref()
|
||||
&& std::ptr::eq(k.doc.as_ptr(), Arc::as_ptr(&self.doc))
|
||||
&& k.indices == indices
|
||||
&& k.outside == outside
|
||||
{
|
||||
return k.any;
|
||||
}
|
||||
#[cfg(test)]
|
||||
ART_TO_FIT_MEASURES.with(|c| c.set(c.get() + 1));
|
||||
// One renderer measures every artboard: each object once.
|
||||
let mut r = vectorcraft_render::Renderer::new();
|
||||
let any = indices.iter().filter_map(|i| self.doc.artboards.get(*i)).any(|a| !a.locked && r.art_bounds_on(&self.doc, a.rect).is_some());
|
||||
*last = Some(ArtToFit { doc: Arc::downgrade(&self.doc), indices: indices.to_vec(), outside, any });
|
||||
any
|
||||
}
|
||||
/// Unsaved changes: the document differs from the saved one (selection changes and changes that
|
||||
/// are not edits to save don't count, [`DocState::same_content`]).
|
||||
pub fn is_dirty(&self) -> bool {
|
||||
|
||||
@@ -1786,6 +1786,38 @@ fn delete_empty_artboards_check_measures_a_document_once() {
|
||||
assert_eq!(delete_empty_enabled_counted(&s), (true, 0), "after another change of the selection");
|
||||
}
|
||||
|
||||
/// Whether Fit to Artwork Bounds has art to fit on `indices`, as a menu asks for it, with the
|
||||
/// plug-ins' and fonts' generations `outside`, and how many times asking measured a document on
|
||||
/// this thread.
|
||||
fn art_to_fit_counted(s: &Session, indices: &[usize], outside: (u64, u64)) -> (bool, usize) {
|
||||
let measures = || crate::doc_state::ART_TO_FIT_MEASURES.with(std::cell::Cell::get);
|
||||
let before = measures();
|
||||
let any = s.doc().unwrap().any_art_to_fit_with(indices, outside);
|
||||
(any, measures() - before)
|
||||
}
|
||||
|
||||
/// A menu asks whether Fit to Artwork Bounds has art on every frame while it is open: the answer is
|
||||
/// measured once for a version of the document and the artboards asked about, kept through a change
|
||||
/// of the selection or the view, and measured again after an edit, for other artboards, and when
|
||||
/// the plug-ins or the fonts change.
|
||||
#[test]
|
||||
fn art_to_fit_is_measured_once_for_a_version_of_the_document() {
|
||||
let mut s = row_of_artboards(2);
|
||||
let both = [0, 1];
|
||||
assert_eq!(art_to_fit_counted(&s, &both, (0, 0)), (false, 1), "the first frame");
|
||||
assert_eq!(art_to_fit_counted(&s, &both, (0, 0)), (false, 0), "a later frame");
|
||||
let art = box_at(&mut s, 10.0, 20.0);
|
||||
assert_eq!(art_to_fit_counted(&s, &both, (0, 0)), (true, 1), "an edit puts art on artboard 1");
|
||||
s.execute("select.set", &json!({"ids": [art.0]})).unwrap();
|
||||
assert_eq!(art_to_fit_counted(&s, &both, (0, 0)), (true, 0), "after a change of the selection");
|
||||
s.execute("view.transparencyGrid", &json!({})).unwrap();
|
||||
assert_eq!(art_to_fit_counted(&s, &both, (0, 0)), (true, 0), "after a change of the view");
|
||||
assert_eq!(art_to_fit_counted(&s, &[1], (0, 0)), (false, 1), "other artboards");
|
||||
assert_eq!(art_to_fit_counted(&s, &[1], (1, 0)), (false, 1), "a plug-in installed or removed");
|
||||
assert_eq!(art_to_fit_counted(&s, &[1], (1, 1)), (false, 1), "fonts installed");
|
||||
assert_eq!(art_to_fit_counted(&s, &[1], (1, 1)), (false, 0));
|
||||
}
|
||||
|
||||
/// The enabled state follows an edit, an undo, a redo, an added and a removed artboard, a locked
|
||||
/// artboard, and each change measures the document once.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user