[fix] rotate center (#213)

* fixes rotate center after translating / transforming

* Adds test, fixes issue on undo/redo

* Update tsconfig.base.json
pull/214/head
Steve Ruiz 2021-10-30 10:04:33 +01:00 zatwierdzone przez GitHub
rodzic cb96740dbc
commit 12e425ddc4
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
12 zmienionych plików z 95 dodań i 35 usunięć

Wyświetl plik

@ -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",

Wyświetl plik

@ -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: {},

Wyświetl plik

@ -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)
})
})
})

Wyświetl plik

@ -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<string[], number[]>()
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,

Wyświetl plik

@ -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) => {

Wyświetl plik

@ -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<string, TLDrawShape>
const pageState = TLDR.getPageState(data, data.appState.currentPageId)
const delta = Vec.sub(point, this.origin)
let newBounds = Utils.getTransformedBoundingBox(

Wyświetl plik

@ -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

Wyświetl plik

@ -347,6 +347,10 @@ export class TLDrawState extends StateManager<Data> {
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<Data> {
}
const Session = getSession(type)
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this.session = new Session(this.state, this.viewport, ...args)

Wyświetl plik

@ -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 {

Wyświetl plik

@ -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) {

Wyświetl plik

@ -11,6 +11,7 @@
"forceConsistentCasingInFileNames": true,
"importHelpers": true,
"importsNotUsedAsValues": "error",
"resolveJsonModule": true,
"incremental": true,
"jsx": "preserve",
"lib": ["dom", "esnext"],

Wyświetl plik

@ -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"