From e6b94f3f3792d67f020995dd4434dc1abef0f13d Mon Sep 17 00:00:00 2001 From: xigua Date: Sun, 12 Jan 2025 23:13:46 +0800 Subject: [PATCH] feat: snap to object when resizing --- packages/core/src/ref_line.ts | 4 +- packages/core/src/selected_elements.ts | 1 + packages/core/src/tools/tool_draw_graphics.ts | 11 ++-- .../src/tools/tool_select/tool_select_move.ts | 4 +- .../tools/tool_select/tool_select_resize.ts | 55 ++++++++++++------- packages/geo/src/geo/geo_point.ts | 4 ++ 6 files changed, 50 insertions(+), 29 deletions(-) diff --git a/packages/core/src/ref_line.ts b/packages/core/src/ref_line.ts index 29dfa5b0..91fe4c3c 100644 --- a/packages/core/src/ref_line.ts +++ b/packages/core/src/ref_line.ts @@ -216,7 +216,7 @@ export class RefLine { * update ref line * and return offset */ - getGraphicsSnapOffset(targetPoints: IPoint[]): IPoint | undefined { + getGraphicsSnapOffset(targetPoints: IPoint[]): IPoint { this.toDrawVLines = []; this.toDrawHLines = []; @@ -232,7 +232,7 @@ export class RefLine { // there are no reference graphs if (sortedXs.length === 0 && sortedYs.length === 0) { - return undefined; + return { x: 0, y: 0 }; } let offsetX: number | undefined = undefined; diff --git a/packages/core/src/selected_elements.ts b/packages/core/src/selected_elements.ts index 869f061a..24e213fb 100644 --- a/packages/core/src/selected_elements.ts +++ b/packages/core/src/selected_elements.ts @@ -32,6 +32,7 @@ export class SelectedElements { this.emitItemsChangeIfChanged(prevItems, items); } getItems({ excludeLocked = false } = {}): SuikaGraphics[] { + // TODO: cache items ? if (excludeLocked) { return this.items.filter((item) => !item.isLock()); } diff --git a/packages/core/src/tools/tool_draw_graphics.ts b/packages/core/src/tools/tool_draw_graphics.ts index 21253264..fbe63de8 100644 --- a/packages/core/src/tools/tool_draw_graphics.ts +++ b/packages/core/src/tools/tool_draw_graphics.ts @@ -146,12 +146,11 @@ export abstract class DrawGraphicsTool implements ITool { const offset = this.editor.refLine.getGraphicsSnapOffset([ this.lastDragPoint, ]); - if (offset) { - this.lastDragPoint = { - x: this.lastDragPoint.x + offset.x, - y: this.lastDragPoint.y + offset.y, - }; - } + this.lastDragPoint = { + x: this.lastDragPoint.x + offset.x, + y: this.lastDragPoint.y + offset.y, + }; + this.isDragging = true; this.updateRect(); diff --git a/packages/core/src/tools/tool_select/tool_select_move.ts b/packages/core/src/tools/tool_select/tool_select_move.ts index 1b40812b..3a5c44b6 100644 --- a/packages/core/src/tools/tool_select/tool_select_move.ts +++ b/packages/core/src/tools/tool_select/tool_select_move.ts @@ -157,8 +157,8 @@ export class SelectMoveTool implements IBaseTool { const newWorldTf = cloneDeep( this.originWorldTfMap.get(graphics.attrs.id)!, ); - newWorldTf[4] += dx + (offset?.x ?? 0); - newWorldTf[5] += dy + (offset?.y ?? 0); + newWorldTf[4] += dx + offset.x; + newWorldTf[5] += dy + offset.y; // change parent if (this.prevParent !== newParent) { diff --git a/packages/core/src/tools/tool_select/tool_select_resize.ts b/packages/core/src/tools/tool_select/tool_select_resize.ts index 5ab5d821..695c3c22 100644 --- a/packages/core/src/tools/tool_select/tool_select_resize.ts +++ b/packages/core/src/tools/tool_select/tool_select_resize.ts @@ -34,7 +34,7 @@ export class SelectResizeTool implements IBaseTool { private updatedAttrsMap = new Map>(); - private lastPoint: IPoint | null = null; + private lastDragPoint: IPoint | null = null; private prevLastPoint: IPoint | null = null; private unbind = noop; @@ -98,24 +98,40 @@ export class SelectResizeTool implements IBaseTool { this.editor.commandManager.disableRedoUndo(); this.editor.hostEventManager.disableDelete(); - const enableGripSnap = - this.editor.setting.get('snapToGrid') && - (['nw', 'ne', 'se', 'sw'].includes(this.handleName) || - (['n', 'e', 's', 'w'].includes(this.handleName) && - this.editor.selectedElements.size() > 1) || + const isTwoDirResizeHandle = ['nw', 'ne', 'se', 'sw'].includes( + this.handleName, + ); + const isOneDirResizeWithoutRotateHandle = + ['n', 'e', 's', 'w'].includes(this.handleName) && + (this.editor.selectedElements.size() > 1 || this.editor.selectedElements.getItems()[0].getRotate() % HALF_PI === 0); - this.lastPoint = this.editor.getSceneCursorXY(e); - if (enableGripSnap) { - this.lastPoint = SnapHelper.getSnapPtBySetting( - this.lastPoint, - this.editor.setting, - ); + if (!this.lastDragPoint) { + this.editor.refLine.cacheGraphicsRefLines(); + } + this.lastDragPoint = this.editor.getSceneCursorXY(e); + + if (isTwoDirResizeHandle || isOneDirResizeWithoutRotateHandle) { + if (this.editor.setting.get('snapToGrid')) { + this.lastDragPoint = SnapHelper.getSnapPtBySetting( + this.lastDragPoint, + this.editor.setting, + ); + } + + const objectSnapOffset = this.editor.refLine.getGraphicsSnapOffset([ + this.lastDragPoint, + ]); + + this.lastDragPoint = { + x: this.lastDragPoint.x + objectSnapOffset.x, + y: this.lastDragPoint.y + objectSnapOffset.y, + }; } const prevLastPoint = this.prevLastPoint; - this.prevLastPoint = this.lastPoint; - if (isEqual(prevLastPoint, this.lastPoint)) { + this.prevLastPoint = this.lastDragPoint; + if (isEqual(prevLastPoint, this.lastDragPoint)) { return; } @@ -150,7 +166,7 @@ export class SelectResizeTool implements IBaseTool { private updateSingleGraphics(graphics: SuikaGraphics) { const updatedAttrs = graphics.calcNewAttrsByControlHandle( this.handleName, - this.lastPoint!, + this.lastDragPoint!, this.originAttrsMap.get(graphics.attrs.id)!, this.originWorldTransforms.get(graphics.attrs.id)!, this.editor.hostEventManager.isShiftPressing, @@ -195,7 +211,7 @@ export class SelectResizeTool implements IBaseTool { }; private updateGraphics() { - if (!this.lastPoint) return; + if (!this.lastDragPoint) return; let prependedTransform: Matrix = new Matrix(); @@ -217,7 +233,7 @@ export class SelectResizeTool implements IBaseTool { const updatedTransformRect = resizeRect( this.handleName, - this.lastPoint, + this.lastDragPoint, { width: originAttrs.width, height: originAttrs.height, @@ -253,7 +269,7 @@ export class SelectResizeTool implements IBaseTool { const transformRect = resizeRect( this.handleName, - this.lastPoint, + this.lastDragPoint, { width: startSelectBbox.width, height: startSelectBbox.height, @@ -361,6 +377,7 @@ export class SelectResizeTool implements IBaseTool { this.originAttrsMap = new Map(); this.updatedAttrsMap = new Map(); - this.lastPoint = null; + this.lastDragPoint = null; + this.editor.refLine.clear(); } } diff --git a/packages/geo/src/geo/geo_point.ts b/packages/geo/src/geo/geo_point.ts index 195b5cb5..13bd0419 100644 --- a/packages/geo/src/geo/geo_point.ts +++ b/packages/geo/src/geo/geo_point.ts @@ -21,6 +21,10 @@ export const pointSub = (p1: IPoint, p2: IPoint): IPoint => { }; }; +export const isZeroPoint = (p: IPoint) => { + return p.x === 0 && p.y === 0; +}; + const TOL = 0.0000000001; export const isPointEqual = (p1: IPoint, p2: IPoint, tol = TOL) => {