From 12e425ddc461bbeb89090af597822c32e34de7a1 Mon Sep 17 00:00:00 2001 From: Steve Ruiz Date: Sat, 30 Oct 2021 10:04:33 +0100 Subject: [PATCH] [fix] rotate center (#213) * fixes rotate center after translating / transforming * Adds test, fixes issue on undo/redo * Update tsconfig.base.json --- packages/tldraw/package.json | 2 +- .../command/translate/translate.command.ts | 5 +- .../sessions/rotate/rotate.session.spec.ts | 54 +++++++++++++++++-- .../session/sessions/rotate/rotate.session.ts | 29 +++++----- .../transform-single.session.ts | 1 + .../sessions/transform/transform.session.ts | 3 +- .../sessions/translate/translate.session.ts | 11 +--- packages/tldraw/src/state/tlstate.ts | 5 ++ packages/tldraw/src/types.ts | 8 +++ packages/vec/scripts/dev.js | 3 -- tsconfig.base.json | 1 + yarn.lock | 8 +-- 12 files changed, 95 insertions(+), 35 deletions(-) diff --git a/packages/tldraw/package.json b/packages/tldraw/package.json index d1aa7f27f..8687915a6 100644 --- a/packages/tldraw/package.json +++ b/packages/tldraw/package.json @@ -66,7 +66,7 @@ "@radix-ui/react-tooltip": "^0.1.1", "@stitches/core": "^1.2.5", "@stitches/react": "^1.0.0", - "@tldraw/core": "^0.1.9", + "@tldraw/core": "^0.1.10", "@tldraw/intersect": "^0.0.132", "@tldraw/vec": "^0.0.132", "perfect-freehand": "^1.0.16", diff --git a/packages/tldraw/src/state/command/translate/translate.command.ts b/packages/tldraw/src/state/command/translate/translate.command.ts index fc9d1dac9..9619c42e5 100644 --- a/packages/tldraw/src/state/command/translate/translate.command.ts +++ b/packages/tldraw/src/state/command/translate/translate.command.ts @@ -1,10 +1,13 @@ import { Vec } from '@tldraw/vec' -import type { Data, TLDrawCommand, PagePartial } from '~types' +import { Data, TLDrawCommand, PagePartial, Session } from '~types' import { TLDR } from '~state/tldr' export function translate(data: Data, ids: string[], delta: number[]): TLDrawCommand { const { currentPageId } = data.appState + // Clear session cache + Session.cache.selectedIds = TLDR.getSelectedIds(data, data.appState.currentPageId) + const before: PagePartial = { shapes: {}, bindings: {}, diff --git a/packages/tldraw/src/state/session/sessions/rotate/rotate.session.spec.ts b/packages/tldraw/src/state/session/sessions/rotate/rotate.session.spec.ts index 54f77b4bc..3ffd93a76 100644 --- a/packages/tldraw/src/state/session/sessions/rotate/rotate.session.spec.ts +++ b/packages/tldraw/src/state/session/sessions/rotate/rotate.session.spec.ts @@ -119,16 +119,64 @@ describe('Rotate session', () => { ) ) - tlstate.startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50]) + tlstate.startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50]).completeSession() - const centerAfter = Vec.round( + const centerAfterA = Vec.round( + Utils.getBoundsCenter( + Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id))) + ) + ) + + tlstate.startSession(SessionType.Rotate, [100, 0]).updateSession([50, 0]).completeSession() + + const centerAfterB = Vec.round( Utils.getBoundsCenter( Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id))) ) ) expect(tlstate.getShape('rect1').rotation) - expect(centerBefore).toStrictEqual(centerAfter) + expect(centerBefore).toStrictEqual(centerAfterA) + expect(centerAfterA).toStrictEqual(centerAfterB) + }) + + it.todo('clears the cached center after transforming') + it.todo('clears the cached center after translating') + it.todo('clears the cached center after undoing') + it.todo('clears the cached center after redoing') + it.todo('clears the cached center after any command other than a rotate command, tbh') + + it('changes the center after nudging', () => { + const tlstate = new TLDrawState().loadDocument(mockDocument).select('rect1', 'rect2') + + const centerBefore = Vec.round( + Utils.getBoundsCenter( + Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id))) + ) + ) + + tlstate.startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50]).completeSession() + + const centerAfterA = Vec.round( + Utils.getBoundsCenter( + Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id))) + ) + ) + + expect(tlstate.getShape('rect1').rotation) + expect(centerBefore).toStrictEqual(centerAfterA) + + tlstate.selectAll().nudge([10, 10]) + + tlstate.startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50]).completeSession() + + const centerAfterB = Vec.round( + Utils.getBoundsCenter( + Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id))) + ) + ) + + expect(centerAfterB).not.toStrictEqual(centerAfterA) }) }) }) diff --git a/packages/tldraw/src/state/session/sessions/rotate/rotate.session.ts b/packages/tldraw/src/state/session/sessions/rotate/rotate.session.ts index 5e12aac2b..0388a1e34 100644 --- a/packages/tldraw/src/state/session/sessions/rotate/rotate.session.ts +++ b/packages/tldraw/src/state/session/sessions/rotate/rotate.session.ts @@ -4,8 +4,6 @@ import { Session, SessionType, TLDrawShape, TLDrawStatus } from '~types' import type { Data } from '~types' import { TLDR } from '~state/tldr' -const centerCache = new WeakMap() - export class RotateSession extends Session { static type = SessionType.Rotate status = TLDrawStatus.Transforming @@ -17,6 +15,7 @@ export class RotateSession extends Session { constructor(data: Data, viewport: TLBounds, point: number[]) { super(viewport) + this.origin = point this.snapshot = getRotateSnapshot(data) this.initialAngle = Vec.angle(this.snapshot.commonBoundsCenter, this.origin) @@ -134,19 +133,25 @@ export function getRotateSnapshot(data: Data) { const pageState = TLDR.getPageState(data, currentPageId) const initialShapes = TLDR.getSelectedBranchSnapshot(data, currentPageId) - const commonBoundsCenter = Utils.getFromCache(centerCache, pageState.selectedIds, () => { - if (initialShapes.length === 0) { - throw Error('No selected shapes!') + if (initialShapes.length === 0) { + throw Error('No selected shapes!') + } + + let commonBoundsCenter: number[] + + if (Session.cache.selectedIds === pageState.selectedIds) { + if (Session.cache.center === undefined) { + throw Error('The center was not added to the cache!') } - const shapesBounds = Object.fromEntries( - initialShapes.map((shape) => [shape.id, TLDR.getBounds(shape)]) + commonBoundsCenter = Session.cache.center + } else { + commonBoundsCenter = Utils.getBoundsCenter( + Utils.getCommonBounds(initialShapes.map(TLDR.getBounds)) ) - - const bounds = Utils.getCommonBounds(Object.values(shapesBounds)) - - return Utils.getBoundsCenter(bounds) - }) + Session.cache.selectedIds = pageState.selectedIds + Session.cache.center = commonBoundsCenter + } return { commonBoundsCenter, diff --git a/packages/tldraw/src/state/session/sessions/transform-single/transform-single.session.ts b/packages/tldraw/src/state/session/sessions/transform-single/transform-single.session.ts index f0498fb61..ed9103a8a 100644 --- a/packages/tldraw/src/state/session/sessions/transform-single/transform-single.session.ts +++ b/packages/tldraw/src/state/session/sessions/transform-single/transform-single.session.ts @@ -47,6 +47,7 @@ export class TransformSingleSession extends Session { this.transformType = transformType this.snapshot = getTransformSingleSnapshot(data, transformType) this.isCreate = isCreate + Session.cache.selectedIds = [...this.snapshot.initialShape.id] } start = (data: Data) => { diff --git a/packages/tldraw/src/state/session/sessions/transform/transform.session.ts b/packages/tldraw/src/state/session/sessions/transform/transform.session.ts index b0e6ae505..ec7d55d2f 100644 --- a/packages/tldraw/src/state/session/sessions/transform/transform.session.ts +++ b/packages/tldraw/src/state/session/sessions/transform/transform.session.ts @@ -43,6 +43,7 @@ export class TransformSession extends Session { this.snapshot = getTransformSnapshot(data, transformType) this.isCreate = isCreate this.initialSelectedIds = TLDR.getSelectedIds(data, data.appState.currentPageId) + Session.cache.selectedIds = [...this.initialSelectedIds] } start = (data: Data) => { @@ -59,8 +60,6 @@ export class TransformSession extends Session { const shapes = {} as Record - const pageState = TLDR.getPageState(data, data.appState.currentPageId) - const delta = Vec.sub(point, this.origin) let newBounds = Utils.getTransformedBoundingBox( diff --git a/packages/tldraw/src/state/session/sessions/translate/translate.session.ts b/packages/tldraw/src/state/session/sessions/translate/translate.session.ts index ab0cd7337..a7aff288d 100644 --- a/packages/tldraw/src/state/session/sessions/translate/translate.session.ts +++ b/packages/tldraw/src/state/session/sessions/translate/translate.session.ts @@ -12,7 +12,6 @@ import { GroupShape, SessionType, ArrowBinding, - TLDrawShapeType, } from '~types' import { SLOW_SPEED, SNAP_DISTANCE } from '~constants' import { TLDR } from '~state/tldr' @@ -70,6 +69,7 @@ export class TranslateSession extends Session { this.snapshot = getTranslateSnapshot(data, isLinked) this.isCreate = isCreate this.isLinked = isLinked + Session.cache.selectedIds = [...TLDR.getSelectedIds(data, data.appState.currentPageId)] } start = (data: Data) => { @@ -94,14 +94,7 @@ export class TranslateSession extends Session { } } - update = ( - data: Data, - point: number[], - shiftKey = false, - altKey = false, - metaKey = false, - viewPort = {} as TLBounds - ) => { + update = (data: Data, point: number[], shiftKey = false, altKey = false, metaKey = false) => { const { selectedIds, initialParentChildren, initialShapes, bindingsToDelete } = this.snapshot const { currentPageId } = data.appState diff --git a/packages/tldraw/src/state/tlstate.ts b/packages/tldraw/src/state/tlstate.ts index a66556ef5..4dba590a9 100644 --- a/packages/tldraw/src/state/tlstate.ts +++ b/packages/tldraw/src/state/tlstate.ts @@ -347,6 +347,10 @@ export class TLDrawState extends StateManager { this.clearSelectHistory() } + if (id.startsWith('undo') || id.startsWith('redo')) { + Session.cache.selectedIds = [...this.selectedIds] + } + this._onChange?.(this, state, id) } @@ -1670,6 +1674,7 @@ export class TLDrawState extends StateManager { } const Session = getSession(type) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore this.session = new Session(this.state, this.viewport, ...args) diff --git a/packages/tldraw/src/types.ts b/packages/tldraw/src/types.ts index 617fab4c8..3ca6882e7 100644 --- a/packages/tldraw/src/types.ts +++ b/packages/tldraw/src/types.ts @@ -155,6 +155,14 @@ export abstract class Session { updateViewport = (viewport: TLBounds) => { this.viewport = viewport } + + static cache: { + selectedIds: string[] + center: number[] + } = { + selectedIds: [], + center: [0, 0], + } } export enum TLDrawStatus { diff --git a/packages/vec/scripts/dev.js b/packages/vec/scripts/dev.js index 050de844d..c9dbacf5c 100644 --- a/packages/vec/scripts/dev.js +++ b/packages/vec/scripts/dev.js @@ -11,10 +11,7 @@ async function main() { bundle: true, format: 'cjs', target: 'es6', - jsxFactory: 'React.createElement', - jsxFragment: 'React.Fragment', tsconfig: './tsconfig.json', - external: ['react', 'react-dom'], incremental: true, watch: { onRebuild(error) { diff --git a/tsconfig.base.json b/tsconfig.base.json index 850404245..6f9d5e72e 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -11,6 +11,7 @@ "forceConsistentCasingInFileNames": true, "importHelpers": true, "importsNotUsedAsValues": "error", + "resolveJsonModule": true, "incremental": true, "jsx": "preserve", "lib": ["dom", "esnext"], diff --git a/yarn.lock b/yarn.lock index cd8d7d14a..b6ac0cf9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3722,10 +3722,10 @@ "@babel/runtime" "^7.12.5" "@testing-library/dom" "^8.0.0" -"@tldraw/core@^0.1.9": - version "0.1.9" - resolved "https://registry.yarnpkg.com/@tldraw/core/-/core-0.1.9.tgz#006748ee0c395ef15756ee1aa3c93b7eef109432" - integrity sha512-3vzFceettMZVkVM+ASaAFyFy2YibwnEkIcIiWTmKER+5R0HIEKnwDuH+zXiv+C1dcbagUvArHRw0AiuzA5ft8g== +"@tldraw/core@^0.1.10": + version "0.1.10" + resolved "https://registry.yarnpkg.com/@tldraw/core/-/core-0.1.10.tgz#fe6aa880b619361473ebccb5c932082efb535518" + integrity sha512-kLqCDQHRykWfskMvcbSn3Ll8Ssd46hMmMdTpqw1O3i3AV2zuWDYkdgGD3PO6PRSkkMZyGNr5/zpkU+Yrk5DYZQ== dependencies: "@use-gesture/react" "^10.0.2"