@@ -41,30 +41,70 @@ sealed class CameraTransform with _$CameraTransform {
4141 @Default (1 ) double pixelRatio,
4242 @Default (Offset .zero) Offset position,
4343 @Default (1 ) double size,
44+ @Default (0 ) double rotation,
4445 FrictionState ? friction,
4546 ]) = _CameraTransform ;
4647
4748 CameraTransform withPosition (Offset position) =>
48- CameraTransform (pixelRatio, position, size);
49+ CameraTransform (pixelRatio, position, size, rotation );
4950
5051 CameraTransform withPointPosition (Point <double > position) =>
51- CameraTransform (pixelRatio, position.toOffset (), size);
52+ CameraTransform (pixelRatio, position.toOffset (), size, rotation );
5253
5354 CameraTransform withSize (double size, [Offset cursor = Offset .zero]) {
5455 // Set size and focus on cursor if provided
5556 final double newSize = size.clamp (kMinZoom, kMaxZoom);
56- var mx = localToGlobal (cursor);
57- mx = (mx - position) * newSize;
58-
5957 return CameraTransform (
6058 pixelRatio,
61- position + (mx - cursor) / newSize,
59+ localToGlobal (cursor) - cursor. rotate ( Offset .zero, - rotation ) / newSize,
6260 newSize,
61+ rotation,
62+ );
63+ }
64+
65+ CameraTransform withRotation (double rotation, [Offset cursor = Offset .zero]) {
66+ final normalized = (rotation + pi) % (2 * pi) - pi;
67+ return CameraTransform (
68+ pixelRatio,
69+ localToGlobal (cursor) - cursor.rotate (Offset .zero, - normalized) / size,
70+ size,
71+ normalized,
72+ );
73+ }
74+
75+ Offset localToGlobal (Offset local) =>
76+ local.rotate (Offset .zero, - rotation) / size + position;
77+
78+ Offset globalToLocal (Offset global) =>
79+ ((global - position) * size).rotate (Offset .zero, rotation);
80+
81+ Offset localToGlobalDelta (Offset delta) =>
82+ delta.rotate (Offset .zero, - rotation) / size;
83+
84+ Rect localToGlobalRect (Rect rect) => _transformRect (rect, localToGlobal);
85+
86+ Rect globalToLocalRect (Rect rect) => _transformRect (rect, globalToLocal);
87+
88+ Rect _transformRect (Rect rect, Offset Function (Offset ) transform) {
89+ final points = [
90+ transform (rect.topLeft),
91+ transform (rect.topRight),
92+ transform (rect.bottomLeft),
93+ transform (rect.bottomRight),
94+ ];
95+ return Rect .fromLTRB (
96+ points.map ((point) => point.dx).reduce (min),
97+ points.map ((point) => point.dy).reduce (min),
98+ points.map ((point) => point.dx).reduce (max),
99+ points.map ((point) => point.dy).reduce (max),
63100 );
64101 }
65102
66- Offset localToGlobal (Offset local) => local / size + position;
67- Offset globalToLocal (Offset global) => (global - position) * size;
103+ void applyToCanvas (Canvas canvas) {
104+ canvas.rotate (rotation);
105+ canvas.scale (size);
106+ canvas.translate (- position.dx, - position.dy);
107+ }
68108
69109 double _getFinalTime (
70110 double velocity,
@@ -125,6 +165,7 @@ sealed class CameraTransform with _$CameraTransform {
125165 pixelRatio,
126166 finalPosition,
127167 finalScale,
168+ rotation,
128169 frictionState,
129170 );
130171 }
@@ -134,12 +175,13 @@ sealed class CameraTransform with _$CameraTransform {
134175 pixelRatio,
135176 this .position - position,
136177 this .size - size,
178+ rotation,
137179 null ,
138180 );
139181 }
140182
141183 CameraTransform improve (RenderResolution resolution, Rect rect) {
142- return CameraTransform (pixelRatio, rect.topLeft, size, friction);
184+ return CameraTransform (pixelRatio, rect.topLeft, size, 0 , friction);
143185 }
144186}
145187
@@ -149,14 +191,21 @@ class TransformCubit extends Cubit<CameraTransform> {
149191
150192 void move (Offset delta) => emit (state.withPosition (state.position + delta));
151193
152- void teleport (Offset position, [double ? scale]) =>
153- emit (state.withPosition (position).withSize (scale ?? state.size));
194+ void teleport (Offset position, [double ? scale, double ? rotation]) => emit (
195+ state
196+ .withPosition (position)
197+ .withSize (scale ?? state.size)
198+ .withRotation (rotation ?? state.rotation),
199+ );
154200
155201 void zoom (double delta, [Offset cursor = Offset .zero]) =>
156202 emit (state.withSize (state.size * delta, cursor));
157203
158204 void focus (Offset cursor) => emit (state.withSize (state.size, cursor));
159205
206+ void rotate (double delta, [Offset cursor = Offset .zero]) =>
207+ emit (state.withRotation (state.rotation + delta, cursor));
208+
160209 void reset () => emit (CameraTransform (state.pixelRatio));
161210
162211 void size (double size, [Offset cursor = Offset .zero]) =>
@@ -519,6 +568,18 @@ class TransformCubit extends Cubit<CameraTransform> {
519568 teleport (clamped.position, clamped.size);
520569 }
521570
571+ void rotateConstrained (
572+ double delta, {
573+ required EditorRuntimeContext runtime,
574+ Offset cursor = Offset .zero,
575+ bool force = false ,
576+ }) {
577+ if (delta == 0 || (runtime.viewCubit.state.locks.lockRotation && ! force)) {
578+ return ;
579+ }
580+ rotate (delta, cursor);
581+ }
582+
522583 void sizeConstrained (
523584 double size, {
524585 required EditorRuntimeContext runtime,
0 commit comments