mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 09:33:36 +00:00
Effect › Color Effects › Vector Halftone… (addon.color.halftone, also in Object › Plug-ins › Color Effects) draws its object as the halftone dots Object › Vector Halftone makes, live: mono (one dot colour, or each dot in the art's colour under it) or CMYK screens, the five dot shapes, frequency, angle, invert and clip to the art's outline. - The screens, dots and caps move to vectorcraft_effects::addons::halftone, shared by the command (its behaviour and tests unchanged) and the effect. - The renderer samples the art (vectorcraft_render::addons::halftone) and keeps the dots by the object's content and what it reads from the document, so unchanged art isn't sampled again; past 100000 cells per screen the object draws as it is. - Expand Appearance turns the object into its dots, and the SVG, PDF, EPS and AI exporters write them as paths. - Its dialog reuses the command's fields, without Keep Original. Deferred: stochastic and radial patterns, a dot-gain curve, per-screen separation settings, type or symbols as dots.
82 lines
4.1 KiB
Rust
82 lines
4.1 KiB
Rust
use super::bounds::drawn_bounds;
|
|
use super::{Affine, AppearanceItem, Effect, Frame, Node, NodeKind, RasterFx, Rect, SymbolBoxes, Vec2, effects};
|
|
|
|
fn visible(effects: &[Effect]) -> bool {
|
|
effects.iter().any(|e| e.visible)
|
|
}
|
|
|
|
/// Does this node need the effects path (a path or compound path with visible effects)?
|
|
pub(crate) fn has_fx(n: &Node) -> bool {
|
|
if !matches!(n.kind, NodeKind::Path { guide: false, .. } | NodeKind::Compound { .. }) {
|
|
return false;
|
|
}
|
|
visible(&n.appearance.effects)
|
|
|| n.appearance.items.iter().any(|i| match i {
|
|
AppearanceItem::Fill(f) => visible(&f.effects),
|
|
AppearanceItem::Stroke(s) => visible(&s.effects),
|
|
})
|
|
}
|
|
|
|
/// Does `n` carry visible object-level effects that only apply through its art (type, images,
|
|
/// symbol instances, live objects; Crop Marks and colour adjustments on anything), is it type with
|
|
/// a geometry effect on a fill or stroke of its own or an image with a fill or stroke of its own
|
|
/// that paints a shape ([`effects::has_item_geometry`]), is it a path whose Transform effect copies
|
|
/// it with a gradient or changes its fills and strokes ([`effects::has_transform_art`]), or is it a
|
|
/// group or layer with an appearance of its own (see [`Renderer::draw_object_fx`])?
|
|
pub(crate) fn has_object_fx(n: &Node) -> bool {
|
|
(effects::needs_outline(n) && (visible(&n.appearance.effects) || n.projection().is_some()))
|
|
|| effects::has_item_geometry(n)
|
|
|| effects::has_transform_art(n)
|
|
|| effects::has_container_appearance(n)
|
|
|| effects::has_crop_marks(n)
|
|
|| effects::addons::halftone::has_halftone(n)
|
|
|| effects::has_adjustment(n)
|
|
|| effects::has_revolve(n)
|
|
|| effects::has_solid(n)
|
|
}
|
|
|
|
/// Is `n` a group or layer (whose evaluated art keeps its knockout setting)?
|
|
pub(super) fn is_container(n: &Node) -> bool {
|
|
matches!(n.kind, NodeKind::Group { .. } | NodeKind::Layer { .. })
|
|
}
|
|
|
|
/// Can the art [`Renderer::fx_art`] makes for `n` be made from live objects it evaluates? It can
|
|
/// when `n` is a live object, and when `n` is a group or layer without a Pathfinder effect that
|
|
/// holds live objects and whose geometry effects reshape them or whose own fills and strokes paint
|
|
/// their evaluated outlines ([`effects::evaluate_container`]).
|
|
pub(super) fn evaluates_live(n: &Node) -> bool {
|
|
use vectorcraft_doc::live::{is_live, subtree_has_live};
|
|
let to_members = effects::has_geometry(&n.appearance.effects) || n.appearance.items.iter().any(effects::paints);
|
|
is_live(n) || (is_container(n) && to_members && !effects::has_pathfinder(n) && subtree_has_live(n))
|
|
}
|
|
|
|
/// Can the art [`Renderer::fx_art`] makes for `n` change while `n` stays the same, other than
|
|
/// through live objects it evaluates? It can when `n` holds type (laid out in the fonts installed),
|
|
/// a symbol instance (made from its symbol's art), an object with a 3D effect or a color
|
|
/// adjustment (shaded or recolored through the color settings), or one with Vector Halftone (its
|
|
/// dots sampled from everything it draws), in it or in the opacity masks of
|
|
/// the objects in it.
|
|
pub(super) fn reads_outside(n: &Node) -> bool {
|
|
let mut reads = false;
|
|
n.walk(&mut |c| {
|
|
reads |= matches!(c.kind, NodeKind::Text(_) | NodeKind::SymbolInstance { .. })
|
|
|| effects::has_revolve(c)
|
|
|| effects::has_solid(c)
|
|
|| effects::has_adjustment(c)
|
|
|| effects::addons::halftone::has_halftone(c)
|
|
|| c.extras().mask.as_ref().is_some_and(|m| reads_outside(&m.art))
|
|
});
|
|
reads
|
|
}
|
|
|
|
/// The [`Content::bounds`] of `n` under raster effects `rfx`: its [`drawn_bounds`] when `rfx`
|
|
/// holds a Rasterize.
|
|
pub(super) fn rasterize_bounds(symbols: &SymbolBoxes, n: &Node, rfx: &[RasterFx]) -> Option<Rect> {
|
|
rfx.iter().any(|fx| matches!(fx, RasterFx::Rasterize(_))).then(|| drawn_bounds(symbols, n)).flatten()
|
|
}
|
|
|
|
/// `f` with the content moved by (`dx`, `dy`) document units (a shadow's offset).
|
|
pub(super) fn shifted<'a>(f: &Frame<'a>, dx: f64, dy: f64) -> Frame<'a> {
|
|
Frame { view: f.view * Affine::translate((dx, dy)), visible: f.visible - Vec2::new(dx, dy), ..*f }
|
|
}
|