diff --git a/packages/editor/api-report.md b/packages/editor/api-report.md index 17654b188..49fffb37c 100644 --- a/packages/editor/api-report.md +++ b/packages/editor/api-report.md @@ -827,9 +827,9 @@ export class Editor extends EventEmitter { moveShapesToPage(shapes: TLShape[], pageId: TLPageId): this; // (undocumented) moveShapesToPage(ids: TLShapeId[], pageId: TLPageId): this; - nudgeShapes(shapes: TLShape[], offset: VecLike, historyOptions?: CommandHistoryOptions): this; + nudgeShapes(shapes: TLShape[], offset: VecLike, historyOptions?: TLCommandHistoryOptions): this; // (undocumented) - nudgeShapes(ids: TLShapeId[], offset: VecLike, historyOptions?: CommandHistoryOptions): this; + nudgeShapes(ids: TLShapeId[], offset: VecLike, historyOptions?: TLCommandHistoryOptions): this; get onlySelectedShape(): null | TLShape; get openMenus(): string[]; packShapes(shapes: TLShape[], gap: number): this; @@ -859,9 +859,9 @@ export class Editor extends EventEmitter { registerExternalContentHandler(type: T, handler: ((info: T extends TLExternalContent_2['type'] ? TLExternalContent_2 & { type: T; } : TLExternalContent_2) => void) | null): this; - renamePage(page: TLPage, name: string, historyOptions?: CommandHistoryOptions): this; + renamePage(page: TLPage, name: string, historyOptions?: TLCommandHistoryOptions): this; // (undocumented) - renamePage(id: TLPageId, name: string, historyOptions?: CommandHistoryOptions): this; + renamePage(id: TLPageId, name: string, historyOptions?: TLCommandHistoryOptions): this; get renderingBounds(): Box2d; get renderingBoundsExpanded(): Box2d; renderingBoundsMargin: number; @@ -910,9 +910,9 @@ export class Editor extends EventEmitter { sendToBack(ids: TLShapeId[]): this; setCamera(point: VecLike, animation?: TLAnimationOptions): this; setCroppingShapeId(id: null | TLShapeId): this; - setCurrentPage(page: TLPage, historyOptions?: CommandHistoryOptions): this; + setCurrentPage(page: TLPage, historyOptions?: TLCommandHistoryOptions): this; // (undocumented) - setCurrentPage(pageId: TLPageId, historyOptions?: CommandHistoryOptions): this; + setCurrentPage(pageId: TLPageId, historyOptions?: TLCommandHistoryOptions): this; setCurrentTool(id: string, info?: {}): this; setCursor: (cursor: Partial) => this; setEditingShapeId(id: null | TLShapeId): this; @@ -920,9 +920,9 @@ export class Editor extends EventEmitter { setFocusedGroupId(next: null | TLShapeId): this; setHintingIds(ids: TLShapeId[]): this; setHoveredShapeId(id: null | TLShapeId): this; - setOpacity(opacity: number, historyOptions?: CommandHistoryOptions): this; - setSelectedShapeIds(ids: TLShapeId[], historyOptions?: CommandHistoryOptions): this; - setStyle(style: StyleProp, value: T, historyOptions?: CommandHistoryOptions): this; + setOpacity(opacity: number, historyOptions?: TLCommandHistoryOptions): this; + setSelectedShapeIds(ids: TLShapeId[], historyOptions?: TLCommandHistoryOptions): this; + setStyle(style: StyleProp, value: T, historyOptions?: TLCommandHistoryOptions): this; shapeUtils: { readonly [K in string]?: ShapeUtil; }; @@ -959,14 +959,14 @@ export class Editor extends EventEmitter { // (undocumented) ungroupShapes(ids: TLShape[]): this; updateAssets(assets: TLAssetPartial[]): this; - updateCurrentPageState(partial: Partial>, historyOptions?: CommandHistoryOptions): this; + updateCurrentPageState(partial: Partial>, historyOptions?: TLCommandHistoryOptions): this; updateDocumentSettings(settings: Partial): this; - updateInstanceState(partial: Partial>, historyOptions?: CommandHistoryOptions): this; - updatePage(partial: RequiredKeys, historyOptions?: CommandHistoryOptions): this; + updateInstanceState(partial: Partial>, historyOptions?: TLCommandHistoryOptions): this; + updatePage(partial: RequiredKeys, historyOptions?: TLCommandHistoryOptions): this; // @internal updateRenderingBounds(): this; - updateShape(partial: null | TLShapePartial | undefined, historyOptions?: CommandHistoryOptions): this; - updateShapes(partials: (null | TLShapePartial | undefined)[], historyOptions?: CommandHistoryOptions): this; + updateShape(partial: null | TLShapePartial | undefined, historyOptions?: TLCommandHistoryOptions): this; + updateShapes(partials: (null | TLShapePartial | undefined)[], historyOptions?: TLCommandHistoryOptions): this; updateViewportScreenBounds(center?: boolean): this; readonly user: UserPreferencesManager; get viewportPageBounds(): Box2d; diff --git a/packages/editor/src/lib/editor/Editor.ts b/packages/editor/src/lib/editor/Editor.ts index 393be5d2b..246fd1f00 100644 --- a/packages/editor/src/lib/editor/Editor.ts +++ b/packages/editor/src/lib/editor/Editor.ts @@ -105,7 +105,7 @@ import { parentsToChildren } from './derivations/parentsToChildren' import { deriveShapeIdsInCurrentPage } from './derivations/shapeIdsInCurrentPage' import { ClickManager } from './managers/ClickManager' import { EnvironmentManager } from './managers/EnvironmentManager' -import { CommandHistoryOptions, HistoryManager } from './managers/HistoryManager' +import { HistoryManager } from './managers/HistoryManager' import { SideEffectManager } from './managers/SideEffectManager' import { SnapManager } from './managers/SnapManager' import { TextManager } from './managers/TextManager' @@ -122,6 +122,7 @@ import { SvgExportContext, SvgExportDef } from './types/SvgExportContext' import { TLContent } from './types/clipboard-types' import { TLEventMap } from './types/emit-types' import { TLEventInfo, TLPinchEventInfo, TLPointerEventInfo } from './types/event-types' +import { TLCommandHistoryOptions } from './types/history-types' import { OptionalKeys, RequiredKeys } from './types/misc-types' import { TLResizeHandle } from './types/selection-types' @@ -1185,7 +1186,7 @@ export class Editor extends EventEmitter { */ updateInstanceState( partial: Partial>, - historyOptions?: CommandHistoryOptions + historyOptions?: TLCommandHistoryOptions ): this { this._updateInstanceState(partial, { ephemeral: true, squashing: true, ...historyOptions }) @@ -1207,7 +1208,7 @@ export class Editor extends EventEmitter { 'updateInstanceState', ( partial: Partial>, - historyOptions?: CommandHistoryOptions + historyOptions?: TLCommandHistoryOptions ) => { const prev = this.instanceState const next = { ...prev, ...partial } @@ -1368,7 +1369,7 @@ export class Editor extends EventEmitter { partial: Partial< Omit >, - historyOptions?: CommandHistoryOptions + historyOptions?: TLCommandHistoryOptions ): this { this._setInstancePageState(partial, historyOptions) return this @@ -1379,7 +1380,7 @@ export class Editor extends EventEmitter { 'setInstancePageState', ( partial: Partial>, - historyOptions?: CommandHistoryOptions + historyOptions?: TLCommandHistoryOptions ) => { const prev = this.store.get(partial.id ?? this.currentPageState.id)! return { data: { prev, partial }, ...historyOptions } @@ -1417,7 +1418,7 @@ export class Editor extends EventEmitter { * * @public */ - setSelectedShapeIds(ids: TLShapeId[], historyOptions?: CommandHistoryOptions): this { + setSelectedShapeIds(ids: TLShapeId[], historyOptions?: TLCommandHistoryOptions): this { this._setSelectedShapeIds(ids, historyOptions) return this } @@ -1425,7 +1426,7 @@ export class Editor extends EventEmitter { /** @internal */ private _setSelectedShapeIds = this.history.createCommand( 'setSelectedShapeIds', - (ids: TLShapeId[], historyOptions?: CommandHistoryOptions) => { + (ids: TLShapeId[], historyOptions?: TLCommandHistoryOptions) => { const { selectedShapeIds: prevSelectedShapeIds } = this.currentPageState const prevSet = new Set(prevSelectedShapeIds) @@ -2693,8 +2694,8 @@ export class Editor extends EventEmitter { const { screenBounds } = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)! const { x: cx, y: cy, z: cz = 1 } = this.camera return { - x: (point.x - screenBounds.x - cx) / cz, - y: (point.y - screenBounds.y - cy) / cz, + x: (point.x - screenBounds.x) / cz - cx, + y: (point.y - screenBounds.y) / cz - cy, z: point.z ?? 0.5, } } @@ -2716,8 +2717,8 @@ export class Editor extends EventEmitter { const { x: cx, y: cy, z: cz = 1 } = this.camera return { - x: point.x * cz + cx + screenBounds.x, - y: point.y * cz + cy + screenBounds.y, + x: (point.x + cx) * cz + screenBounds.x, + y: (point.y + cy) * cz + screenBounds.y, z: point.z ?? 0.5, } } @@ -3218,9 +3219,9 @@ export class Editor extends EventEmitter { * * @public */ - setCurrentPage(page: TLPage, historyOptions?: CommandHistoryOptions): this - setCurrentPage(pageId: TLPageId, historyOptions?: CommandHistoryOptions): this - setCurrentPage(arg: TLPageId | TLPage, historyOptions?: CommandHistoryOptions): this { + setCurrentPage(page: TLPage, historyOptions?: TLCommandHistoryOptions): this + setCurrentPage(pageId: TLPageId, historyOptions?: TLCommandHistoryOptions): this + setCurrentPage(arg: TLPageId | TLPage, historyOptions?: TLCommandHistoryOptions): this { const pageId = typeof arg === 'string' ? arg : arg.id this._setCurrentPageId(pageId, historyOptions) return this @@ -3228,7 +3229,7 @@ export class Editor extends EventEmitter { /** @internal */ private _setCurrentPageId = this.history.createCommand( 'setCurrentPage', - (pageId: TLPageId, historyOptions?: CommandHistoryOptions) => { + (pageId: TLPageId, historyOptions?: TLCommandHistoryOptions) => { if (!this.store.has(pageId)) { console.error("Tried to set the current page id to a page that doesn't exist.") return @@ -3295,14 +3296,14 @@ export class Editor extends EventEmitter { * * @public */ - updatePage(partial: RequiredKeys, historyOptions?: CommandHistoryOptions): this { + updatePage(partial: RequiredKeys, historyOptions?: TLCommandHistoryOptions): this { this._updatePage(partial, historyOptions) return this } /** @internal */ private _updatePage = this.history.createCommand( 'updatePage', - (partial: RequiredKeys, historyOptions?: CommandHistoryOptions) => { + (partial: RequiredKeys, historyOptions?: TLCommandHistoryOptions) => { if (this.instanceState.isReadonly) return null const prev = this.getPage(partial.id) @@ -3513,9 +3514,9 @@ export class Editor extends EventEmitter { * * @public */ - renamePage(page: TLPage, name: string, historyOptions?: CommandHistoryOptions): this - renamePage(id: TLPageId, name: string, historyOptions?: CommandHistoryOptions): this - renamePage(arg: TLPageId | TLPage, name: string, historyOptions?: CommandHistoryOptions) { + renamePage(page: TLPage, name: string, historyOptions?: TLCommandHistoryOptions): this + renamePage(id: TLPageId, name: string, historyOptions?: TLCommandHistoryOptions): this + renamePage(arg: TLPageId | TLPage, name: string, historyOptions?: TLCommandHistoryOptions) { const id = typeof arg === 'string' ? arg : arg.id if (this.instanceState.isReadonly) return this this.updatePage({ id, name }, historyOptions) @@ -4966,12 +4967,12 @@ export class Editor extends EventEmitter { * @param direction - The direction in which to move the shapes. * @param historyOptions - (optional) The history options for the change. */ - nudgeShapes(shapes: TLShape[], offset: VecLike, historyOptions?: CommandHistoryOptions): this - nudgeShapes(ids: TLShapeId[], offset: VecLike, historyOptions?: CommandHistoryOptions): this + nudgeShapes(shapes: TLShape[], offset: VecLike, historyOptions?: TLCommandHistoryOptions): this + nudgeShapes(ids: TLShapeId[], offset: VecLike, historyOptions?: TLCommandHistoryOptions): this nudgeShapes( arg: TLShapeId[] | TLShape[], offset: VecLike, - historyOptions?: CommandHistoryOptions + historyOptions?: TLCommandHistoryOptions ): this { const ids = typeof arg[0] === 'string' ? (arg as TLShapeId[]) : (arg as TLShape[]).map((s) => s.id) @@ -6815,7 +6816,7 @@ export class Editor extends EventEmitter { */ updateShape( partial: TLShapePartial | null | undefined, - historyOptions?: CommandHistoryOptions + historyOptions?: TLCommandHistoryOptions ) { this.updateShapes([partial], historyOptions) return this @@ -6836,7 +6837,7 @@ export class Editor extends EventEmitter { */ updateShapes( partials: (TLShapePartial | null | undefined)[], - historyOptions?: CommandHistoryOptions + historyOptions?: TLCommandHistoryOptions ) { let compactedPartials = compact(partials) if (this.animatingShapes.size > 0) { @@ -6859,7 +6860,10 @@ export class Editor extends EventEmitter { /** @internal */ private _updateShapes = this.history.createCommand( 'updateShapes', - (_partials: (TLShapePartial | null | undefined)[], historyOptions?: CommandHistoryOptions) => { + ( + _partials: (TLShapePartial | null | undefined)[], + historyOptions?: TLCommandHistoryOptions + ) => { if (this.instanceState.isReadonly) return null const partials = compact(_partials) @@ -7205,7 +7209,7 @@ export class Editor extends EventEmitter { * @param opacity - The opacity to set. Must be a number between 0 and 1 inclusive. * @param historyOptions - The history options for the change. */ - setOpacity(opacity: number, historyOptions?: CommandHistoryOptions): this { + setOpacity(opacity: number, historyOptions?: TLCommandHistoryOptions): this { this.history.batch(() => { if (this.isIn('select')) { const { @@ -7269,7 +7273,7 @@ export class Editor extends EventEmitter { * * @public */ - setStyle(style: StyleProp, value: T, historyOptions?: CommandHistoryOptions): this { + setStyle(style: StyleProp, value: T, historyOptions?: TLCommandHistoryOptions): this { this.history.batch(() => { if (this.isIn('select')) { const { diff --git a/packages/editor/src/lib/editor/managers/HistoryManager.test.ts b/packages/editor/src/lib/editor/managers/HistoryManager.test.ts index 0de4c0a6e..67748d064 100644 --- a/packages/editor/src/lib/editor/managers/HistoryManager.test.ts +++ b/packages/editor/src/lib/editor/managers/HistoryManager.test.ts @@ -1,4 +1,5 @@ -import { CommandHistoryOptions, HistoryManager } from './HistoryManager' +import { TLCommandHistoryOptions } from '../types/history-types' +import { HistoryManager } from './HistoryManager' import { stack } from './Stack' function createCounterHistoryManager() { @@ -291,8 +292,8 @@ describe('history options', () => { let manager: HistoryManager let state: { a: number; b: number } - let setA: (n: number, historyOptions?: CommandHistoryOptions) => any - let setB: (n: number, historyOptions?: CommandHistoryOptions) => any + let setA: (n: number, historyOptions?: TLCommandHistoryOptions) => any + let setB: (n: number, historyOptions?: TLCommandHistoryOptions) => any beforeEach(() => { manager = new HistoryManager({ emit: () => void null }, () => { @@ -306,7 +307,7 @@ describe('history options', () => { setA = manager.createCommand( 'setA', - (n: number, historyOptions?: CommandHistoryOptions) => ({ + (n: number, historyOptions?: TLCommandHistoryOptions) => ({ data: { next: n, prev: state.a }, ...historyOptions, }), @@ -323,7 +324,7 @@ describe('history options', () => { setB = manager.createCommand( 'setB', - (n: number, historyOptions?: CommandHistoryOptions) => ({ + (n: number, historyOptions?: TLCommandHistoryOptions) => ({ data: { next: n, prev: state.b }, ...historyOptions, }), diff --git a/packages/editor/src/lib/editor/managers/HistoryManager.ts b/packages/editor/src/lib/editor/managers/HistoryManager.ts index 0b96b6d2c..f38ea946c 100644 --- a/packages/editor/src/lib/editor/managers/HistoryManager.ts +++ b/packages/editor/src/lib/editor/managers/HistoryManager.ts @@ -1,29 +1,13 @@ import { atom, transact } from '@tldraw/state' import { devFreeze } from '@tldraw/store' import { uniqueId } from '../../utils/uniqueId' -import { TLCommandHandler, TLHistoryEntry } from '../types/history-types' +import { TLCommandHandler, TLCommandHistoryOptions, TLHistoryEntry } from '../types/history-types' import { Stack, stack } from './Stack' -/** @public */ -export type CommandHistoryOptions = Partial<{ - /** - * When true, this command will be squashed with the previous command in the undo / redo stack. - */ - squashing: boolean - /** - * When true, this command will not add anything to the undo / redo stack. Its change will never be undone or redone. - */ - ephemeral: boolean - /** - * When true, adding this this command will not clear out the redo stack. - */ - preservesRedoStack: boolean -}> - type CommandFn = (...args: any[]) => | ({ data: Data - } & CommandHistoryOptions) + } & TLCommandHistoryOptions) | null | undefined | void diff --git a/packages/editor/src/lib/editor/types/history-types.ts b/packages/editor/src/lib/editor/types/history-types.ts index 7ccddadd0..0ab03b479 100644 --- a/packages/editor/src/lib/editor/types/history-types.ts +++ b/packages/editor/src/lib/editor/types/history-types.ts @@ -1,3 +1,19 @@ +/** @public */ +export type TLCommandHistoryOptions = Partial<{ + /** + * When true, this command will be squashed with the previous command in the undo / redo stack. + */ + squashing: boolean + /** + * When true, this command will not add anything to the undo / redo stack. Its change will never be undone or redone. + */ + ephemeral: boolean + /** + * When true, adding this this command will not clear out the redo stack. + */ + preservesRedoStack: boolean +}> + /** @public */ export type TLHistoryMark = { type: 'STOP' diff --git a/packages/tldraw/src/test/commands/screenToPage.test.ts b/packages/tldraw/src/test/commands/screenToPage.test.ts index b7560a824..7416490a3 100644 --- a/packages/tldraw/src/test/commands/screenToPage.test.ts +++ b/packages/tldraw/src/test/commands/screenToPage.test.ts @@ -1,3 +1,4 @@ +import { VecLike } from '@tldraw/editor' import { TestEditor } from '../TestEditor' let editor: TestEditor @@ -6,126 +7,70 @@ beforeEach(() => { editor = new TestEditor() }) +function checkScreenPage(screen: VecLike, page: VecLike) { + const pageResult = editor.screenToPage(screen) + expect(pageResult).toMatchObject(page) + const screenResult = editor.pageToScreen(pageResult) + expect(screenResult).toMatchObject(screen) +} + describe('viewport.screenToPage', () => { it('converts correctly', () => { - expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.pageToScreen({ x: 0, y: 0 })).toMatchObject({ x: 0, y: 0 }) - - expect(editor.screenToPage({ x: 100, y: 100 })).toMatchObject({ x: 100, y: 100 }) - expect(editor.pageToScreen({ x: 100, y: 100 })).toMatchObject({ x: 100, y: 100 }) - - expect(editor.screenToPage({ x: -100, y: -100 })).toMatchObject({ x: -100, y: -100 }) - expect(editor.pageToScreen({ x: -100, y: -100 })).toMatchObject({ x: -100, y: -100 }) + checkScreenPage({ x: 0, y: 0 }, { x: 0, y: 0 }) + checkScreenPage({ x: 100, y: 100 }, { x: 100, y: 100 }) + checkScreenPage({ x: -100, y: -100 }, { x: -100, y: -100 }) }) it('converts correctly when zoomed', () => { editor.setCamera({ x: 0, y: 0, z: 0.5 }) - expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.pageToScreen({ x: 0, y: 0 })).toMatchObject({ x: 0, y: 0 }) - - expect(editor.screenToPage({ x: 100, y: 100 })).toMatchObject({ x: 200, y: 200 }) - expect(editor.pageToScreen({ x: 200, y: 200 })).toMatchObject({ x: 100, y: 100 }) - - expect(editor.screenToPage({ x: -100, y: -100 })).toMatchObject({ x: -200, y: -200 }) - expect(editor.pageToScreen({ x: -200, y: -200 })).toMatchObject({ x: -100, y: -100 }) + checkScreenPage({ x: 0, y: 0 }, { x: 0, y: 0 }) + checkScreenPage({ x: 100, y: 100 }, { x: 200, y: 200 }) + checkScreenPage({ x: -100, y: -100 }, { x: -200, y: -200 }) }) it('converts correctly when panned', () => { editor.setCamera({ x: 100, y: 100 }) - expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: -100, y: -100 }) - expect(editor.pageToScreen({ x: -100, y: -100 })).toMatchObject({ x: 0, y: 0 }) - - expect(editor.screenToPage({ x: 100, y: 100 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.pageToScreen({ x: 0, y: 0 })).toMatchObject({ x: 100, y: 100 }) - - expect(editor.screenToPage({ x: -100, y: -100 })).toMatchObject({ x: -200, y: -200 }) - expect(editor.pageToScreen({ x: -200, y: -200 })).toMatchObject({ x: -100, y: -100 }) + checkScreenPage({ x: 0, y: 0 }, { x: -100, y: -100 }) + checkScreenPage({ x: 100, y: 100 }, { x: 0, y: 0 }) + checkScreenPage({ x: -100, y: -100 }, { x: -200, y: -200 }) }) it('converts correctly when panned and zoomed', () => { editor.setCamera({ x: 100, y: 100, z: 0.5 }) - expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: -200, y: -200 }) - expect(editor.pageToScreen({ x: -200, y: -200 })).toMatchObject({ x: 0, y: 0 }) - - expect(editor.screenToPage({ x: 100, y: 100 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.pageToScreen({ x: 0, y: 0 })).toMatchObject({ x: 100, y: 100 }) - - expect(editor.screenToPage({ x: -100, y: -100 })).toMatchObject({ x: -400, y: -400 }) - expect(editor.pageToScreen({ x: -400, y: -400 })).toMatchObject({ x: -100, y: -100 }) + checkScreenPage({ x: 0, y: 0 }, { x: -100, y: -100 }) + checkScreenPage({ x: 100, y: 100 }, { x: 100, y: 100 }) + checkScreenPage({ x: -100, y: -100 }, { x: -300, y: -300 }) + checkScreenPage({ x: -150, y: -150 }, { x: -400, y: -400 }) }) it('converts correctly when offset', () => { - // move the editor's page bounds down and to the left by 100, 100 - // 0,0 s - // +------------------------+ - // | 100,100 s | - // | c-----------------+ | - // | | 0,0 p | | - // | | | | - editor.updateInstanceState({ screenBounds: { ...editor.viewportScreenBounds, x: 100, y: 100 } }) - expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: -100, y: -100 }) - expect(editor.pageToScreen({ x: -100, y: -100 })).toMatchObject({ x: 0, y: 0 }) - - expect(editor.screenToPage({ x: -100, y: -100 })).toMatchObject({ x: -200, y: -200 }) - expect(editor.pageToScreen({ x: -200, y: -200 })).toMatchObject({ x: -100, y: -100 }) - - expect(editor.screenToPage({ x: 100, y: 100 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.pageToScreen({ x: 0, y: 0 })).toMatchObject({ x: 100, y: 100 }) - - // 0,0 s - // c------------------------+ - // | 100,100 s | - // | +-----------------+ | - // | | 100,100 p | | - // | | | | - - editor.setCamera({ x: -100, y: -100 }) // -100, -100 - expect(editor.screenToPage({ x: -100, y: -100 })).toMatchObject({ x: -100, y: -100 }) - expect(editor.pageToScreen({ x: -100, y: -100 })).toMatchObject({ x: -100, y: -100 }) - expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.pageToScreen({ x: 0, y: 0 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.screenToPage({ x: 100, y: 100 })).toMatchObject({ x: 100, y: 100 }) - expect(editor.pageToScreen({ x: 100, y: 100 })).toMatchObject({ x: 100, y: 100 }) - - // 0,0 s no offset, zoom at 50% - // c------------------------+ - // | 0,0 p | - // | | - // | | - // | | - editor.setCamera({ x: 0, y: 0, z: 0.5 }) - editor.updateInstanceState({ screenBounds: { ...editor.viewportScreenBounds, x: 0, y: 0 } }) - expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.pageToScreen({ x: 0, y: 0 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.screenToPage({ x: -100, y: -100 })).toMatchObject({ x: -200, y: -200 }) - expect(editor.pageToScreen({ x: -200, y: -200 })).toMatchObject({ x: -100, y: -100 }) - expect(editor.screenToPage({ x: 100, y: 100 })).toMatchObject({ x: 200, y: 200 }) - expect(editor.pageToScreen({ x: 200, y: 200 })).toMatchObject({ x: 100, y: 100 }) + checkScreenPage({ x: 0, y: 0 }, { x: -100, y: -100 }) + checkScreenPage({ x: -100, y: -100 }, { x: -200, y: -200 }) + checkScreenPage({ x: 100, y: 100 }, { x: 0, y: 0 }) }) it('converts correctly when zoomed out', () => { // camera at zero, screenbounds at zero, but zoom at .5 editor.setCamera({ x: 0, y: 0, z: 0.5 }) editor.updateInstanceState({ screenBounds: { ...editor.viewportScreenBounds, x: 0, y: 0 } }) - expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.pageToScreen({ x: 0, y: 0 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.screenToPage({ x: -100, y: -100 })).toMatchObject({ x: -200, y: -200 }) - expect(editor.pageToScreen({ x: -200, y: -200 })).toMatchObject({ x: -100, y: -100 }) - expect(editor.screenToPage({ x: 100, y: 100 })).toMatchObject({ x: 200, y: 200 }) - expect(editor.pageToScreen({ x: 200, y: 200 })).toMatchObject({ x: 100, y: 100 }) + + checkScreenPage({ x: 0, y: 0 }, { x: 0, y: 0 }) + checkScreenPage({ x: -100, y: -100 }, { x: -200, y: -200 }) + checkScreenPage({ x: 100, y: 100 }, { x: 200, y: 200 }) }) it('converts correctly when zoomed in', () => { editor.setCamera({ x: 0, y: 0, z: 2 }) editor.updateInstanceState({ screenBounds: { ...editor.viewportScreenBounds, x: 0, y: 0 } }) - expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.screenToPage({ x: -100, y: -100 })).toMatchObject({ x: -50, y: -50 }) - expect(editor.screenToPage({ x: 100, y: 100 })).toMatchObject({ x: 50, y: 50 }) + + checkScreenPage({ x: 0, y: 0 }, { x: 0, y: 0 }) + checkScreenPage({ x: -100, y: -100 }, { x: -50, y: -50 }) + checkScreenPage({ x: 100, y: 100 }, { x: 50, y: 50 }) }) it('converts correctly when zoomed', () => { @@ -133,82 +78,63 @@ describe('viewport.screenToPage', () => { editor.updateInstanceState({ screenBounds: { ...editor.viewportScreenBounds, x: 0, y: 0 } }) editor.setCamera({ x: 0, y: 0, z: 0.5 }) - // zero point, where page and screen are the same - expect(editor.pageToScreen({ x: 0, y: 0 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: 0, y: 0 }) + checkScreenPage({ x: 0, y: 0 }, { x: 0, y: 0 }) + checkScreenPage({ x: -100, y: -100 }, { x: -200, y: -200 }) + checkScreenPage({ x: 100, y: 100 }, { x: 200, y: 200 }) + }) - expect(editor.pageToScreen({ x: 100, y: 100 })).toMatchObject({ x: 50, y: 50 }) - expect(editor.screenToPage({ x: 50, y: 50 })).toMatchObject({ x: 100, y: 100 }) + it('converts correctly when offset and zoomed', () => { + editor.setCamera({ x: 0, y: 0, z: 0.5 }) + editor.updateInstanceState({ screenBounds: { ...editor.viewportScreenBounds, x: 100, y: 100 } }) - expect(editor.pageToScreen({ x: 200, y: 200 })).toMatchObject({ x: 100, y: 100 }) - expect(editor.screenToPage({ x: 100, y: 100 })).toMatchObject({ x: 200, y: 200 }) + checkScreenPage({ x: 0, y: 0 }, { x: -200, y: -200 }) + checkScreenPage({ x: -100, y: -100 }, { x: -400, y: -400 }) + checkScreenPage({ x: 100, y: 100 }, { x: 0, y: 0 }) }) it('converts correctly when zoomed and panned', () => { editor.updateInstanceState({ screenBounds: { ...editor.viewportScreenBounds, x: 0, y: 0 } }) editor.setCamera({ x: 100, y: 100, z: 0.5 }) - expect(editor.pageToScreen({ x: 0, y: 0 })).toMatchObject({ x: 100, y: 100 }) - expect(editor.screenToPage({ x: 100, y: 100 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.pageToScreen({ x: 100, y: 100 })).toMatchObject({ x: 150, y: 150 }) - expect(editor.screenToPage({ x: 150, y: 150 })).toMatchObject({ x: 100, y: 100 }) - - // zero point, where page and screen are the same - expect(editor.pageToScreen({ x: 200, y: 200 })).toMatchObject({ x: 200, y: 200 }) - expect(editor.screenToPage({ x: 200, y: 200 })).toMatchObject({ x: 200, y: 200 }) + checkScreenPage({ x: 0, y: 0 }, { x: -100, y: -100 }) + checkScreenPage({ x: -100, y: -100 }, { x: -300, y: -300 }) + checkScreenPage({ x: 100, y: 100 }, { x: 100, y: 100 }) }) it('converts correctly when offset', () => { editor.updateInstanceState({ screenBounds: { ...editor.viewportScreenBounds, x: 100, y: 100 } }) editor.setCamera({ x: 0, y: 0, z: 0.5 }) - expect(editor.pageToScreen({ x: 0, y: 0 })).toMatchObject({ x: 100, y: 100 }) - expect(editor.pageToScreen({ x: 100, y: 100 })).toMatchObject({ x: 150, y: 150 }) - expect(editor.pageToScreen({ x: 200, y: 200 })).toMatchObject({ x: 200, y: 200 }) - - expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: -200, y: -200 }) - expect(editor.screenToPage({ x: 100, y: 100 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.screenToPage({ x: 200, y: 200 })).toMatchObject({ x: 200, y: 200 }) - expect(editor.screenToPage({ x: 300, y: 300 })).toMatchObject({ x: 400, y: 400 }) + checkScreenPage({ x: 0, y: 0 }, { x: -200, y: -200 }) + checkScreenPage({ x: 100, y: 100 }, { x: 0, y: 0 }) + checkScreenPage({ x: 200, y: 200 }, { x: 200, y: 200 }) }) it('converts correctly when panned', () => { editor.updateInstanceState({ screenBounds: { ...editor.viewportScreenBounds, x: 0, y: 0 } }) editor.setCamera({ x: 100, y: 100, z: 1 }) - expect(editor.pageToScreen({ x: 0, y: 0 })).toMatchObject({ x: 100, y: 100 }) - expect(editor.pageToScreen({ x: 100, y: 100 })).toMatchObject({ x: 200, y: 200 }) - expect(editor.pageToScreen({ x: 200, y: 200 })).toMatchObject({ x: 300, y: 300 }) - - expect(editor.screenToPage({ x: 100, y: 100 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.screenToPage({ x: 200, y: 200 })).toMatchObject({ x: 100, y: 100 }) - expect(editor.screenToPage({ x: 300, y: 300 })).toMatchObject({ x: 200, y: 200 }) + checkScreenPage({ x: 0, y: 0 }, { x: -100, y: -100 }) + checkScreenPage({ x: 100, y: 100 }, { x: 0, y: 0 }) + checkScreenPage({ x: 200, y: 200 }, { x: 100, y: 100 }) }) it('converts correctly when panned and zoomed', () => { editor.updateInstanceState({ screenBounds: { ...editor.viewportScreenBounds, x: 0, y: 0 } }) editor.setCamera({ x: 100, y: 100, z: 0.5 }) - expect(editor.pageToScreen({ x: 0, y: 0 })).toMatchObject({ x: 100, y: 100 }) - expect(editor.pageToScreen({ x: 100, y: 100 })).toMatchObject({ x: 150, y: 150 }) - expect(editor.pageToScreen({ x: 200, y: 200 })).toMatchObject({ x: 200, y: 200 }) - - expect(editor.screenToPage({ x: 100, y: 100 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.screenToPage({ x: 150, y: 150 })).toMatchObject({ x: 100, y: 100 }) - expect(editor.screenToPage({ x: 200, y: 200 })).toMatchObject({ x: 200, y: 200 }) + checkScreenPage({ x: 0, y: 0 }, { x: -100, y: -100 }) + checkScreenPage({ x: 100, y: 100 }, { x: 100, y: 100 }) + checkScreenPage({ x: 200, y: 200 }, { x: 300, y: 300 }) }) it('converts correctly when panned and zoomed and offset', () => { editor.updateInstanceState({ screenBounds: { ...editor.viewportScreenBounds, x: 100, y: 100 } }) editor.setCamera({ x: 100, y: 100, z: 0.5 }) - expect(editor.pageToScreen({ x: 0, y: 0 })).toMatchObject({ x: 200, y: 200 }) - expect(editor.pageToScreen({ x: 100, y: 100 })).toMatchObject({ x: 250, y: 250 }) - expect(editor.pageToScreen({ x: 200, y: 200 })).toMatchObject({ x: 300, y: 300 }) - - expect(editor.screenToPage({ x: 200, y: 200 })).toMatchObject({ x: 0, y: 0 }) - expect(editor.screenToPage({ x: 250, y: 250 })).toMatchObject({ x: 100, y: 100 }) - expect(editor.screenToPage({ x: 300, y: 300 })).toMatchObject({ x: 200, y: 200 }) + checkScreenPage({ x: 0, y: 0 }, { x: -300, y: -300 }) + checkScreenPage({ x: 100, y: 100 }, { x: -100, y: -100 }) + checkScreenPage({ x: 200, y: 200 }, { x: 100, y: 100 }) }) }) diff --git a/packages/tldraw/src/test/commands/updateViewportPageBounds.test.ts b/packages/tldraw/src/test/commands/updateViewportPageBounds.test.ts index fa8e9afc7..60914d98f 100644 --- a/packages/tldraw/src/test/commands/updateViewportPageBounds.test.ts +++ b/packages/tldraw/src/test/commands/updateViewportPageBounds.test.ts @@ -84,7 +84,7 @@ describe('When center is false', () => { editor.setCamera({ x: -100, y: -100, z: 1 }) expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: 100, y: 100 }) editor.setCamera({ x: -100, y: -100, z: 2 }) - expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: 50, y: 50 }) + expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: 100, y: 100 }) editor.setScreenBounds({ x: 100, y: 100, w: 500, h: 600 }, false) expect(editor.viewportScreenBounds).toMatchObject({ @@ -93,7 +93,7 @@ describe('When center is false', () => { w: 500, h: 600, }) - expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: 0, y: 0 }) + expect(editor.screenToPage({ x: 0, y: 0 })).toMatchObject({ x: 50, y: 50 }) }) })