mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-26 10:47:11 -06:00
Window > Asset Export lists the document's assets as thumbnails: art
dragged in off the canvas (or added with +) becomes one asset per object,
or one of them all with Alt; Object > Collect for Export does the same.
Assets name their objects, so they follow edits (moving the art moves the
crop) and deleting the art removes it from its asset. Click selects,
double-click renames, the bin removes. The panel's export settings are
Export for Screens' own format rows on the document's shared settings, and
Export writes each asset alone, cropped to its art, into a picked folder
(the web downloads a zip). Export for Screens' Assets tab lists the assets
with checkboxes and exports the checked ones.
Commands: assets.add, assets.remove, assets.rename, assets.list,
assets.export, assets.settings.set; document.exportForScreens takes
assets. Model: Document.assets (ExportAsset {id, name, nodes}).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
58 lines
2.3 KiB
Rust
58 lines
2.3 KiB
Rust
//! Asset Export (Window → Asset Export, Object → Collect for Export): pieces of art collected to
|
|
//! export on their own, at the export settings Export for Screens shares.
|
|
//!
|
|
//! An asset names objects rather than copying them: it follows edits (a moved object moves the
|
|
//! crop, a recoloured one exports recoloured) and lets go of objects that are deleted
|
|
//! ([`Document::prune_assets`]; an asset left with none goes too). Asset ids come from the
|
|
//! document's id counter, so they never repeat within a document.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{Document, NodeId};
|
|
|
|
/// One asset: what it is called (its files' names) and the objects it exports, cropped to their
|
|
/// visual bounds.
|
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
pub struct ExportAsset {
|
|
pub id: u64,
|
|
pub name: String,
|
|
pub nodes: Vec<NodeId>,
|
|
}
|
|
|
|
/// What the assets are named when they aren't named after their object: `Asset 1`, `Asset 2`…
|
|
const ASSET: &str = "Asset ";
|
|
|
|
impl Document {
|
|
/// Asset `id`.
|
|
pub fn asset(&self, id: u64) -> Option<&ExportAsset> {
|
|
self.assets.iter().find(|a| a.id == id)
|
|
}
|
|
|
|
/// The name of the next unnamed asset: one more than the highest `Asset N` so far.
|
|
pub fn next_asset_name(&self) -> String {
|
|
let n = self.assets.iter().filter_map(|a| a.name.strip_prefix(ASSET)?.parse::<u64>().ok()).max().unwrap_or(0);
|
|
format!("{ASSET}{}", n.saturating_add(1))
|
|
}
|
|
|
|
/// Forget the objects of assets that are no longer in the document, and the assets left with
|
|
/// none (after every edit).
|
|
pub fn prune_assets(&mut self) {
|
|
if self.assets.iter().all(|a| a.nodes.iter().all(|id| self.node(*id).is_some())) {
|
|
return;
|
|
}
|
|
let mut assets = std::mem::take(&mut self.assets);
|
|
for a in &mut assets {
|
|
a.nodes.retain(|id| self.node(*id).is_some());
|
|
}
|
|
assets.retain(|a| !a.nodes.is_empty());
|
|
self.assets = assets;
|
|
}
|
|
|
|
/// `ids` that are in the document, back to front.
|
|
pub fn paint_order(&self, ids: impl IntoIterator<Item = NodeId>) -> Vec<NodeId> {
|
|
let mut v: Vec<(Vec<usize>, NodeId)> = ids.into_iter().filter_map(|id| self.index_path(id).map(|p| (p, id))).collect();
|
|
v.sort();
|
|
v.into_iter().map(|(_, id)| id).collect()
|
|
}
|
|
}
|