mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-26 19:18:14 -06:00
Viewer: the shape tools show the tool's own shape while dragging
The drag preview was a 1 px axis-aligned rectangle for every shape tool, which read as no preview at all against most comps. Draw the tool's own path instead (rectangle, rounded rectangle, ellipse, polygon, star), from the engine's own geometry, filled with the Tools bar's Fill and outlined in its Stroke, with a caption saying where the shape lands — a mask on the selected layer, into the selected shape layer, or a new shape layer. The ghost and the released shape go through one normalisation, so the drag shows exactly what it commits: Alt (Option) draws from the press point and Shift squares the box, both live. The drag also starts at the press point now, not where the 6 px drag threshold was crossed, so the shape is drawn from where the button went down, as in After Effects.
This commit is contained in:
@@ -205,8 +205,10 @@ pub(crate) fn shape_group(ids: &mut Ids, kind: &str, size: [f64; 2], paint: &Sha
|
||||
Some(build::shape_group(ids, gname, items))
|
||||
}
|
||||
|
||||
/// The mask a shape tool draws: `kind` of `size` centred at `centre` (layer space).
|
||||
pub(crate) fn mask_path(kind: &str, centre: [f64; 2], size: [f64; 2]) -> Option<ShapePath> {
|
||||
/// The path a shape tool draws: `kind` of `size` centred at `centre` (layer space). The geometry
|
||||
/// of every one of the five kinds, so a caller that only wants to draw it (the viewer's
|
||||
/// drag ghost) cannot drift from what `shape.newShape` / `layer.addMask` commit.
|
||||
pub fn mask_path(kind: &str, centre: [f64; 2], size: [f64; 2]) -> Option<ShapePath> {
|
||||
let bez = match kind {
|
||||
"rect" | "rectangle" => return Some(ShapePath::rect(centre, size[0], size[1])),
|
||||
"ellipse" => return Some(ShapePath::ellipse(centre, size[0], size[1])),
|
||||
@@ -219,12 +221,18 @@ pub(crate) fn mask_path(kind: &str, centre: [f64; 2], size: [f64; 2]) -> Option<
|
||||
effectcraft_path::from_kurbo(&bez).into_iter().next()
|
||||
}
|
||||
|
||||
/// The layer a shape tool draws into when no `layer` is given: the first selected unlocked shape
|
||||
/// layer, else none (a new shape layer).
|
||||
pub fn selected_shape_target(s: &Session, comp: &Comp) -> Option<LayerId> {
|
||||
s.state.selected_layers.iter().copied().find(|l| comp.layer(*l).is_some_and(|l| matches!(l.source, LayerSource::Shape) && !l.switches.locked))
|
||||
}
|
||||
|
||||
/// The shape layer a shape tool or the Pen draws into: `layer` (which must be a shape layer),
|
||||
/// else the first selected unlocked shape layer, else none (a new shape layer).
|
||||
pub(crate) fn draw_target(s: &Session, comp: &Comp, p: &Value, cmd: &str) -> Result<Option<LayerId>> {
|
||||
let target = match p.get("layer") {
|
||||
Some(l) => Some(super::resolve_layer(comp, l).ok_or_else(|| bad(cmd, format!("no layer {l}")))?),
|
||||
None => s.state.selected_layers.iter().copied().find(|l| comp.layer(*l).is_some_and(|l| matches!(l.source, LayerSource::Shape) && !l.switches.locked)),
|
||||
None => selected_shape_target(s, comp),
|
||||
};
|
||||
if target.and_then(|l| comp.layer(l)).is_some_and(|l| !matches!(l.source, LayerSource::Shape)) {
|
||||
return Err(bad(cmd, "the layer is not a shape layer"));
|
||||
|
||||
Reference in New Issue
Block a user