[fix] rotate center (#213)
* fixes rotate center after translating / transforming * Adds test, fixes issue on undo/redo * Update tsconfig.base.json
This commit is contained in:
parent
cb96740dbc
commit
12e425ddc4
12 changed files with 95 additions and 35 deletions
|
@ -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",
|
||||
|
|
|
@ -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: {},
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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!')
|
||||
}
|
||||
|
||||
const shapesBounds = Object.fromEntries(
|
||||
initialShapes.map((shape) => [shape.id, TLDR.getBounds(shape)])
|
||||
let commonBoundsCenter: number[]
|
||||
|
||||
if (Session.cache.selectedIds === pageState.selectedIds) {
|
||||
if (Session.cache.center === undefined) {
|
||||
throw Error('The center was not added to the cache!')
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"forceConsistentCasingInFileNames": true,
|
||||
"importHelpers": true,
|
||||
"importsNotUsedAsValues": "error",
|
||||
"resolveJsonModule": true,
|
||||
"incremental": true,
|
||||
"jsx": "preserve",
|
||||
"lib": ["dom", "esnext"],
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
Loading…
Reference in a new issue