Skip to content

Commit ba28076

Browse files
committed
fix(edgeless): rotate the vertices points in polygone edition mode to copy vertices positions
1 parent 6ee2139 commit ba28076

File tree

2 files changed

+119
-19
lines changed

2 files changed

+119
-19
lines changed

β€Žpackages/affine/gfx/shape/src/element-view.tsβ€Ž

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
ShapeElementModel,
99
ShapeType,
1010
} from '@blocksuite/affine-model';
11-
import { Bound } from '@blocksuite/global/gfx';
11+
import { Bound, rotatePoint } from '@blocksuite/global/gfx';
1212
import type { GfxModel } from '@blocksuite/std/gfx';
1313
import {
1414
GfxElementModelView,
@@ -250,10 +250,15 @@ export class ShapeElementView extends GfxElementModelView<ShapeElementModel> {
250250
// Record vertex absolute model position
251251
const bound = Bound.deserialize(this.model.xywh);
252252
const v = verts[this._pendingVertexIndex];
253-
this._vertexDragStartModelCoord = [
253+
const localCoord: [number, number] = [
254254
bound.x + v[0] * bound.w,
255255
bound.y + v[1] * bound.h,
256256
];
257+
this._vertexDragStartModelCoord = rotatePoint(
258+
localCoord,
259+
bound.center as [number, number],
260+
this.model.rotate ?? 0
261+
) as [number, number];
257262

258263
this._surfaceComponent?.refresh();
259264
return;
@@ -276,7 +281,12 @@ export class ShapeElementView extends GfxElementModelView<ShapeElementModel> {
276281
const cp = this._vertexEditingOverlay!.getBezierControlPoints(this._pendingBezierHandle.vertexIndex);
277282
if (cp) {
278283
const pt = this._pendingBezierHandle.handleIndex === 0 ? cp.cp1 : cp.cp2;
279-
this._bezierDragStartModelCoord = pt;
284+
const bound = Bound.deserialize(this.model.xywh);
285+
this._bezierDragStartModelCoord = rotatePoint(
286+
pt,
287+
bound.center as [number, number],
288+
this.model.rotate ?? 0
289+
) as [number, number];
280290
}
281291

282292
this._surfaceComponent?.refresh();

β€Žpackages/affine/gfx/shape/src/overlay/polygon-vertex-editing-overlay.tsβ€Ž

Lines changed: 106 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,26 @@ export class PolygonVertexEditingOverlay extends ToolOverlay {
162162
const zoom = this.gfx.viewport.zoom;
163163
const hitDist = VERTEX_HIT_DISTANCE / zoom;
164164

165+
// Inverse-rotate incoming world coordinates into polygon's local space
166+
let localX = modelX;
167+
let localY = modelY;
168+
const rotate = model.rotate ?? 0;
169+
if (rotate) {
170+
const cx = bound.x + bound.w / 2;
171+
const cy = bound.y + bound.h / 2;
172+
const rad = -(rotate * Math.PI) / 180; // negative for inverse rotation
173+
const cos = Math.cos(rad);
174+
const sin = Math.sin(rad);
175+
const dx = modelX - cx;
176+
const dy = modelY - cy;
177+
localX = cx + dx * cos - dy * sin;
178+
localY = cy + dx * sin + dy * cos;
179+
}
180+
165181
for (let i = 0; i < model.vertices.length; i++) {
166182
const [ax, ay] = this._toAbsolute(model.vertices[i], bound);
167-
const dx = modelX - ax;
168-
const dy = modelY - ay;
183+
const dx = localX - ax;
184+
const dy = localY - ay;
169185
if (Math.sqrt(dx * dx + dy * dy) < hitDist) {
170186
return i;
171187
}
@@ -190,25 +206,39 @@ export class PolygonVertexEditingOverlay extends ToolOverlay {
190206
const zoom = this.gfx.viewport.zoom;
191207
const snapDist = SNAP_GUIDE_DISTANCE / zoom;
192208

193-
// Collect absolute positions of all other vertices for snapping
209+
// Inverse-rotate mouse position from world space to local (unrotated) space
210+
const rotate = model.rotate ?? 0;
211+
let localX = modelX;
212+
let localY = modelY;
213+
if (rotate) {
214+
const cx = bound.x + bound.w / 2;
215+
const cy = bound.y + bound.h / 2;
216+
const rad = (-rotate * Math.PI) / 180; // negative for inverse
217+
const dx = modelX - cx;
218+
const dy = modelY - cy;
219+
localX = cx + dx * Math.cos(rad) - dy * Math.sin(rad);
220+
localY = cy + dx * Math.sin(rad) + dy * Math.cos(rad);
221+
}
222+
223+
// Collect local-space absolute positions of all other vertices for snapping
194224
const otherAbsolute: [number, number][] = [];
195225
for (let i = 0; i < model.vertices.length; i++) {
196226
if (i === vertexIndex) continue;
197227
otherAbsolute.push(this._toAbsolute(model.vertices[i], bound));
198228
}
199229

200-
// Attempt coordinate snapping
201-
let snappedX = modelX;
202-
let snappedY = modelY;
230+
// Attempt coordinate snapping in local (unrotated) space
231+
let snappedX = localX;
232+
let snappedY = localY;
203233
this.snapGuideX = null;
204234
this.snapGuideY = null;
205235

206236
for (const [ox, oy] of otherAbsolute) {
207-
if (Math.abs(modelX - ox) < snapDist) {
237+
if (Math.abs(localX - ox) < snapDist) {
208238
snappedX = ox;
209239
this.snapGuideX = ox;
210240
}
211-
if (Math.abs(modelY - oy) < snapDist) {
241+
if (Math.abs(localY - oy) < snapDist) {
212242
snappedY = oy;
213243
this.snapGuideY = oy;
214244
}
@@ -359,15 +389,31 @@ export class PolygonVertexEditingOverlay extends ToolOverlay {
359389
const zoom = this.gfx.viewport.zoom;
360390
const hitDist = MIDPOINT_HIT_DISTANCE / zoom;
361391

392+
// Inverse-rotate incoming world coordinates into polygon's local space
393+
let localX = modelX;
394+
let localY = modelY;
395+
const rotate = model.rotate ?? 0;
396+
if (rotate) {
397+
const cx = bound.x + bound.w / 2;
398+
const cy = bound.y + bound.h / 2;
399+
const rad = -(rotate * Math.PI) / 180; // negative for inverse rotation
400+
const cos = Math.cos(rad);
401+
const sin = Math.sin(rad);
402+
const dx = modelX - cx;
403+
const dy = modelY - cy;
404+
localX = cx + dx * cos - dy * sin;
405+
localY = cy + dx * sin + dy * cos;
406+
}
407+
362408
for (let i = 0; i < model.vertices.length; i++) {
363409
const [ax, ay] = this._toAbsolute(model.vertices[i], bound);
364410
const nextIdx = (i + 1) % model.vertices.length;
365411
const [bx, by] = this._toAbsolute(model.vertices[nextIdx], bound);
366412
const mx = (ax + bx) / 2;
367413
const my = (ay + by) / 2;
368414

369-
const dx = modelX - mx;
370-
const dy = modelY - my;
415+
const dx = localX - mx;
416+
const dy = localY - my;
371417
if (Math.sqrt(dx * dx + dy * dy) < hitDist) {
372418
return i;
373419
}
@@ -386,24 +432,41 @@ export class PolygonVertexEditingOverlay extends ToolOverlay {
386432
const model = this._getPolygonModel();
387433
if (!model || !model.vertices || !model.smoothFlags) return null;
388434

435+
const bound = Bound.deserialize(model.xywh);
389436
const zoom = this.gfx.viewport.zoom;
390437
const hitDist = BEZIER_HANDLE_RADIUS * 2 / zoom;
391438

439+
// Inverse-rotate incoming world coordinates into polygon's local space
440+
let localX = modelX;
441+
let localY = modelY;
442+
const rotate = model.rotate ?? 0;
443+
if (rotate) {
444+
const cx = bound.x + bound.w / 2;
445+
const cy = bound.y + bound.h / 2;
446+
const rad = (-rotate * Math.PI) / 180;
447+
const cos = Math.cos(rad);
448+
const sin = Math.sin(rad);
449+
const dx = modelX - cx;
450+
const dy = modelY - cy;
451+
localX = cx + dx * cos - dy * sin;
452+
localY = cy + dx * sin + dy * cos;
453+
}
454+
392455
for (let i = 0; i < model.vertices.length; i++) {
393456
if (!model.smoothFlags[i]) continue;
394457
const cp = this.getBezierControlPoints(i);
395458
if (!cp) continue;
396459

397460
// Check cp1
398-
const dx1 = modelX - cp.cp1[0];
399-
const dy1 = modelY - cp.cp1[1];
461+
const dx1 = localX - cp.cp1[0];
462+
const dy1 = localY - cp.cp1[1];
400463
if (Math.sqrt(dx1 * dx1 + dy1 * dy1) < hitDist) {
401464
return { vertexIndex: i, handleIndex: 0 };
402465
}
403466

404467
// Check cp2
405-
const dx2 = modelX - cp.cp2[0];
406-
const dy2 = modelY - cp.cp2[1];
468+
const dx2 = localX - cp.cp2[0];
469+
const dy2 = localY - cp.cp2[1];
407470
if (Math.sqrt(dx2 * dx2 + dy2 * dy2) < hitDist) {
408471
return { vertexIndex: i, handleIndex: 1 };
409472
}
@@ -427,6 +490,22 @@ export class PolygonVertexEditingOverlay extends ToolOverlay {
427490
const bound = Bound.deserialize(model.xywh);
428491
const count = model.vertices.length;
429492

493+
// Inverse-rotate mouse position from world space to local (unrotated) space
494+
const rotate = model.rotate ?? 0;
495+
let localX = modelX;
496+
let localY = modelY;
497+
if (rotate) {
498+
const cx = bound.x + bound.w / 2;
499+
const cy = bound.y + bound.h / 2;
500+
const rad = -(rotate * Math.PI) / 180;
501+
const cos = Math.cos(rad);
502+
const sin = Math.sin(rad);
503+
const dx = modelX - cx;
504+
const dy = modelY - cy;
505+
localX = cx + dx * cos - dy * sin;
506+
localY = cy + dx * sin + dy * cos;
507+
}
508+
430509
// Initialize controlPoints array if null
431510
let controlPoints: (number[] | null)[] = model.controlPoints
432511
? [...model.controlPoints]
@@ -449,8 +528,8 @@ export class PolygonVertexEditingOverlay extends ToolOverlay {
449528
}
450529

451530
// Convert absolute position to normalized coordinates (in current bound)
452-
const normX = (modelX - bound.x) / bound.w;
453-
const normY = (modelY - bound.y) / bound.h;
531+
const normX = (localX - bound.x) / bound.w;
532+
const normY = (localY - bound.y) / bound.h;
454533

455534
// Update the specific handle
456535
const entry = [...controlPoints[vertexIndex]!];
@@ -984,6 +1063,17 @@ export class PolygonVertexEditingOverlay extends ToolOverlay {
9841063
ctx.save();
9851064
ctx.globalAlpha = this.globalAlpha;
9861065

1066+
// ── Apply rotation around shape center ─────────────────────────
1067+
const rotate = model.rotate ?? 0;
1068+
if (rotate) {
1069+
const cx = bound.x + bound.w / 2;
1070+
const cy = bound.y + bound.h / 2;
1071+
const rad = (rotate * Math.PI) / 180;
1072+
ctx.translate(cx, cy);
1073+
ctx.rotate(rad);
1074+
ctx.translate(-cx, -cy);
1075+
}
1076+
9871077
// ── Draw snap guides ────────────────────────────────────────────
9881078
if (this.activeVertexIndex >= 0) {
9891079
ctx.setLineDash([4 / zoom, 3 / zoom]);

0 commit comments

Comments
Β (0)