mirror of
https://github.com/penpot/penpot.git
synced 2026-07-23 14:38:12 +00:00
⚡ Translation-only fast path in Shape::transform_selrect and Path::transform
This commit is contained in:
parent
9fd26d7473
commit
a5c4b83611
@ -1363,6 +1363,41 @@ impl Shape {
|
||||
}
|
||||
|
||||
fn transform_selrect(&mut self, transform: &Matrix) {
|
||||
// Translation-only fast path. The general code rebuilds bounds
|
||||
// by transforming all four corners (`bounds.transform`), then
|
||||
// re-derives self.transform from those bounds. For pure
|
||||
// translation, selrect just shifts and self.transform doesn't
|
||||
// change shape (only its translation component). Doing this
|
||||
// cheaply per-shape during a layout drag matters because
|
||||
// `Shape::apply_transform` is called once per modified shape
|
||||
// per pointer-move (N+1 times for layouts with N children).
|
||||
let eps = 1e-4_f32;
|
||||
let is_translation_only = (transform.scale_x() - 1.0).abs() < eps
|
||||
&& (transform.scale_y() - 1.0).abs() < eps
|
||||
&& transform.skew_x().abs() < eps
|
||||
&& transform.skew_y().abs() < eps
|
||||
&& transform.persp_x().abs() < eps
|
||||
&& transform.persp_y().abs() < eps;
|
||||
|
||||
if is_translation_only {
|
||||
let tx = transform.translate_x();
|
||||
let ty = transform.translate_y();
|
||||
// For pure translation, only `selrect` shifts — `self.transform`
|
||||
// represents the shape's rotation/scale *around its center*,
|
||||
// which translation leaves untouched. The general path
|
||||
// confirms this: it derives `self.transform` from
|
||||
// `bounds.transform_matrix()` after a translate, which still
|
||||
// yields the original rotation matrix (translation lives in
|
||||
// bounds position, not in `transform_matrix`).
|
||||
self.selrect = math::Rect::from_xywh(
|
||||
self.selrect.left + tx,
|
||||
self.selrect.top + ty,
|
||||
self.selrect.width(),
|
||||
self.selrect.height(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut center = self.selrect.center();
|
||||
center = transform.map_point(center);
|
||||
|
||||
|
||||
@ -236,6 +236,46 @@ impl Path {
|
||||
}
|
||||
|
||||
pub fn transform(&mut self, mtx: &Matrix) {
|
||||
// Translation-only fast path. During interactive translation
|
||||
// drags the layout's children all receive the same pure-
|
||||
// translation modifier, and `Path::transform` is called once
|
||||
// per child per pointer-move. The general path walks every
|
||||
// segment doing `mtx.map_point(...)` (a 2x3 matrix multiply
|
||||
// per control point) plus a Skia `make_transform` (full path
|
||||
// walk in C++). For pure translation that work reduces to
|
||||
// simple x+=tx, y+=ty additions plus Skia's much cheaper
|
||||
// `with_offset` op. Saves a meaningful slice of per-rAF cost
|
||||
// on path-heavy layouts (icons, vector illustrations, etc).
|
||||
let eps = 1e-4_f32;
|
||||
let is_translation_only = (mtx.scale_x() - 1.0).abs() < eps
|
||||
&& (mtx.scale_y() - 1.0).abs() < eps
|
||||
&& mtx.skew_x().abs() < eps
|
||||
&& mtx.skew_y().abs() < eps
|
||||
&& mtx.persp_x().abs() < eps
|
||||
&& mtx.persp_y().abs() < eps;
|
||||
|
||||
if is_translation_only {
|
||||
let tx = mtx.translate_x();
|
||||
let ty = mtx.translate_y();
|
||||
self.segments.iter_mut().for_each(|s| match s {
|
||||
Segment::MoveTo(p) | Segment::LineTo(p) => {
|
||||
p.0 += tx;
|
||||
p.1 += ty;
|
||||
}
|
||||
Segment::CurveTo((c1, c2, p)) => {
|
||||
c1.0 += tx;
|
||||
c1.1 += ty;
|
||||
c2.0 += tx;
|
||||
c2.1 += ty;
|
||||
p.0 += tx;
|
||||
p.1 += ty;
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
self.skia_path = self.skia_path.with_offset((tx, ty));
|
||||
return;
|
||||
}
|
||||
|
||||
self.segments.iter_mut().for_each(|s| match s {
|
||||
Segment::MoveTo(p) => {
|
||||
let np = mtx.map_point(skia::Point::new(p.0, p.1));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user