@tsdraw/core 0.8.2 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -63,7 +63,6 @@ interface TsdrawSessionStateSnapshot {
63
63
  x: number;
64
64
  y: number;
65
65
  zoom: number;
66
- rotation?: number;
67
66
  };
68
67
  currentToolId: string;
69
68
  drawStyle: {
@@ -150,8 +149,31 @@ declare class InputManager {
150
149
  getInputs(): PointerInput;
151
150
  }
152
151
 
152
+ interface Viewport {
153
+ x: number;
154
+ y: number;
155
+ zoom: number;
156
+ }
157
+ declare function createViewport(): Viewport;
158
+ declare function screenToPage(viewport: Viewport, screenX: number, screenY: number): {
159
+ x: number;
160
+ y: number;
161
+ };
162
+ declare function pageToScreen(viewport: Viewport, pageX: number, pageY: number): {
163
+ x: number;
164
+ y: number;
165
+ };
166
+ declare function setViewport(viewport: Viewport, updater: {
167
+ x?: number;
168
+ y?: number;
169
+ zoom?: number;
170
+ }): Viewport;
171
+ declare function panViewport(viewport: Viewport, dx: number, dy: number): Viewport;
172
+ declare function zoomViewport(viewport: Viewport, factor: number, centerX?: number, centerY?: number): Viewport;
173
+
153
174
  interface IEditor {
154
175
  getZoomLevel(): number;
176
+ readonly viewport: Viewport;
155
177
  options: {
156
178
  dragDistanceSquared: number;
157
179
  };
@@ -181,15 +203,20 @@ interface IEditor {
181
203
  fill: FillStyle;
182
204
  size: SizeStyle;
183
205
  }>): void;
206
+ setViewport(partial: Partial<Viewport>): void;
184
207
  panBy(dx: number, dy: number): void;
185
208
  }
186
209
 
187
210
  interface ToolPointerDownInfo {
188
211
  point: Vec3;
212
+ screenX?: number;
213
+ screenY?: number;
189
214
  }
190
215
  interface ToolPointerMoveInfo {
191
216
  screenDeltaX?: number;
192
217
  screenDeltaY?: number;
218
+ screenX?: number;
219
+ screenY?: number;
193
220
  }
194
221
  interface ToolKeyInfo {
195
222
  key: string;
@@ -218,30 +245,26 @@ declare abstract class StateNode {
218
245
  onInterrupt(): void;
219
246
  }
220
247
 
221
- interface Viewport {
222
- x: number;
223
- y: number;
224
- zoom: number;
225
- rotation: number;
226
- }
227
- declare function createViewport(): Viewport;
228
- declare function screenToPage(viewport: Viewport, screenX: number, screenY: number): {
248
+ interface CameraPanSession {
249
+ initialViewportX: number;
250
+ initialViewportY: number;
251
+ originScreenX: number;
252
+ originScreenY: number;
253
+ velocityX: number;
254
+ velocityY: number;
255
+ previousScreenX: number;
256
+ previousScreenY: number;
257
+ lastMoveTime: number;
258
+ }
259
+ declare function beginCameraPan(viewport: Viewport, screenX: number, screenY: number): CameraPanSession;
260
+ declare function moveCameraPan(session: CameraPanSession, currentScreenX: number, currentScreenY: number): {
229
261
  x: number;
230
262
  y: number;
231
263
  };
232
- declare function pageToScreen(viewport: Viewport, pageX: number, pageY: number): {
233
- x: number;
234
- y: number;
235
- };
236
- declare function setViewport(viewport: Viewport, updater: {
237
- x?: number;
238
- y?: number;
239
- zoom?: number;
240
- rotation?: number;
241
- }): Viewport;
242
- declare function panViewport(viewport: Viewport, dx: number, dy: number): Viewport;
243
- declare function zoomViewport(viewport: Viewport, factor: number, centerX?: number, centerY?: number): Viewport;
244
- declare function rotateViewport(viewport: Viewport, delta: number, centerX?: number, centerY?: number): Viewport;
264
+ interface CameraSlideAnimation {
265
+ stop: () => void;
266
+ }
267
+ declare function startCameraSlide(session: CameraPanSession, applyPan: (dx: number, dy: number) => void, onFrame: () => void): CameraSlideAnimation | null;
245
268
 
246
269
  type TsdrawRenderTheme = 'light' | 'dark';
247
270
  declare function resolveThemeColor(colorStyle: string, theme: TsdrawRenderTheme): string;
@@ -348,7 +371,6 @@ declare class Editor {
348
371
  setViewport(partial: Partial<Viewport>): void;
349
372
  panBy(dx: number, dy: number): void;
350
373
  zoomAt(factor: number, screenX: number, screenY: number): void;
351
- rotateAt(delta: number, screenX: number, screenY: number): void;
352
374
  deleteShapes(ids: ShapeId[]): void;
353
375
  getDocumentSnapshot(): TsdrawDocumentSnapshot;
354
376
  loadDocumentSnapshot(snapshot: TsdrawDocumentSnapshot): void;
@@ -613,8 +635,12 @@ declare class HandIdleState extends StateNode {
613
635
 
614
636
  declare class HandDraggingState extends StateNode {
615
637
  static id: string;
638
+ private panSession;
639
+ onEnter(info?: ToolStateTransitionInfo): void;
616
640
  onPointerMove(info?: ToolPointerMoveInfo): void;
641
+ getPanSession(): CameraPanSession | null;
617
642
  onPointerUp(): void;
643
+ onExit(): void;
618
644
  onCancel(): void;
619
645
  onInterrupt(): void;
620
646
  }
@@ -630,4 +656,4 @@ declare function decodePathToPoints(segments: {
630
656
  y: number;
631
657
  }[];
632
658
 
633
- export { type Bounds, CanvasRenderer, CircleDrawingState, CircleIdleState, type ColorStyle, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, type FillStyle, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, SquareDrawingState, SquareIdleState, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawHistorySnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, applyMove, applyResize, applyRotation, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, rotateViewport, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, zoomViewport };
659
+ export { type Bounds, type CameraPanSession, type CameraSlideAnimation, CanvasRenderer, CircleDrawingState, CircleIdleState, type ColorStyle, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, type FillStyle, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, SquareDrawingState, SquareIdleState, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawHistorySnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, applyMove, applyResize, applyRotation, beginCameraPan, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, moveCameraPan, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, startCameraSlide, zoomViewport };
package/dist/index.d.ts CHANGED
@@ -63,7 +63,6 @@ interface TsdrawSessionStateSnapshot {
63
63
  x: number;
64
64
  y: number;
65
65
  zoom: number;
66
- rotation?: number;
67
66
  };
68
67
  currentToolId: string;
69
68
  drawStyle: {
@@ -150,8 +149,31 @@ declare class InputManager {
150
149
  getInputs(): PointerInput;
151
150
  }
152
151
 
152
+ interface Viewport {
153
+ x: number;
154
+ y: number;
155
+ zoom: number;
156
+ }
157
+ declare function createViewport(): Viewport;
158
+ declare function screenToPage(viewport: Viewport, screenX: number, screenY: number): {
159
+ x: number;
160
+ y: number;
161
+ };
162
+ declare function pageToScreen(viewport: Viewport, pageX: number, pageY: number): {
163
+ x: number;
164
+ y: number;
165
+ };
166
+ declare function setViewport(viewport: Viewport, updater: {
167
+ x?: number;
168
+ y?: number;
169
+ zoom?: number;
170
+ }): Viewport;
171
+ declare function panViewport(viewport: Viewport, dx: number, dy: number): Viewport;
172
+ declare function zoomViewport(viewport: Viewport, factor: number, centerX?: number, centerY?: number): Viewport;
173
+
153
174
  interface IEditor {
154
175
  getZoomLevel(): number;
176
+ readonly viewport: Viewport;
155
177
  options: {
156
178
  dragDistanceSquared: number;
157
179
  };
@@ -181,15 +203,20 @@ interface IEditor {
181
203
  fill: FillStyle;
182
204
  size: SizeStyle;
183
205
  }>): void;
206
+ setViewport(partial: Partial<Viewport>): void;
184
207
  panBy(dx: number, dy: number): void;
185
208
  }
186
209
 
187
210
  interface ToolPointerDownInfo {
188
211
  point: Vec3;
212
+ screenX?: number;
213
+ screenY?: number;
189
214
  }
190
215
  interface ToolPointerMoveInfo {
191
216
  screenDeltaX?: number;
192
217
  screenDeltaY?: number;
218
+ screenX?: number;
219
+ screenY?: number;
193
220
  }
194
221
  interface ToolKeyInfo {
195
222
  key: string;
@@ -218,30 +245,26 @@ declare abstract class StateNode {
218
245
  onInterrupt(): void;
219
246
  }
220
247
 
221
- interface Viewport {
222
- x: number;
223
- y: number;
224
- zoom: number;
225
- rotation: number;
226
- }
227
- declare function createViewport(): Viewport;
228
- declare function screenToPage(viewport: Viewport, screenX: number, screenY: number): {
248
+ interface CameraPanSession {
249
+ initialViewportX: number;
250
+ initialViewportY: number;
251
+ originScreenX: number;
252
+ originScreenY: number;
253
+ velocityX: number;
254
+ velocityY: number;
255
+ previousScreenX: number;
256
+ previousScreenY: number;
257
+ lastMoveTime: number;
258
+ }
259
+ declare function beginCameraPan(viewport: Viewport, screenX: number, screenY: number): CameraPanSession;
260
+ declare function moveCameraPan(session: CameraPanSession, currentScreenX: number, currentScreenY: number): {
229
261
  x: number;
230
262
  y: number;
231
263
  };
232
- declare function pageToScreen(viewport: Viewport, pageX: number, pageY: number): {
233
- x: number;
234
- y: number;
235
- };
236
- declare function setViewport(viewport: Viewport, updater: {
237
- x?: number;
238
- y?: number;
239
- zoom?: number;
240
- rotation?: number;
241
- }): Viewport;
242
- declare function panViewport(viewport: Viewport, dx: number, dy: number): Viewport;
243
- declare function zoomViewport(viewport: Viewport, factor: number, centerX?: number, centerY?: number): Viewport;
244
- declare function rotateViewport(viewport: Viewport, delta: number, centerX?: number, centerY?: number): Viewport;
264
+ interface CameraSlideAnimation {
265
+ stop: () => void;
266
+ }
267
+ declare function startCameraSlide(session: CameraPanSession, applyPan: (dx: number, dy: number) => void, onFrame: () => void): CameraSlideAnimation | null;
245
268
 
246
269
  type TsdrawRenderTheme = 'light' | 'dark';
247
270
  declare function resolveThemeColor(colorStyle: string, theme: TsdrawRenderTheme): string;
@@ -348,7 +371,6 @@ declare class Editor {
348
371
  setViewport(partial: Partial<Viewport>): void;
349
372
  panBy(dx: number, dy: number): void;
350
373
  zoomAt(factor: number, screenX: number, screenY: number): void;
351
- rotateAt(delta: number, screenX: number, screenY: number): void;
352
374
  deleteShapes(ids: ShapeId[]): void;
353
375
  getDocumentSnapshot(): TsdrawDocumentSnapshot;
354
376
  loadDocumentSnapshot(snapshot: TsdrawDocumentSnapshot): void;
@@ -613,8 +635,12 @@ declare class HandIdleState extends StateNode {
613
635
 
614
636
  declare class HandDraggingState extends StateNode {
615
637
  static id: string;
638
+ private panSession;
639
+ onEnter(info?: ToolStateTransitionInfo): void;
616
640
  onPointerMove(info?: ToolPointerMoveInfo): void;
641
+ getPanSession(): CameraPanSession | null;
617
642
  onPointerUp(): void;
643
+ onExit(): void;
618
644
  onCancel(): void;
619
645
  onInterrupt(): void;
620
646
  }
@@ -630,4 +656,4 @@ declare function decodePathToPoints(segments: {
630
656
  y: number;
631
657
  }[];
632
658
 
633
- export { type Bounds, CanvasRenderer, CircleDrawingState, CircleIdleState, type ColorStyle, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, type FillStyle, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, SquareDrawingState, SquareIdleState, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawHistorySnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, applyMove, applyResize, applyRotation, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, rotateViewport, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, zoomViewport };
659
+ export { type Bounds, type CameraPanSession, type CameraSlideAnimation, CanvasRenderer, CircleDrawingState, CircleIdleState, type ColorStyle, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, type FillStyle, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, SquareDrawingState, SquareIdleState, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawHistorySnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, applyMove, applyResize, applyRotation, beginCameraPan, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, moveCameraPan, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, startCameraSlide, zoomViewport };
package/dist/index.js CHANGED
@@ -223,34 +223,25 @@ var StateNode = class {
223
223
 
224
224
  // src/canvas/viewport.ts
225
225
  function createViewport() {
226
- return { x: 0, y: 0, zoom: 1, rotation: 0 };
226
+ return { x: 0, y: 0, zoom: 1 };
227
227
  }
228
228
  function screenToPage(viewport, screenX, screenY) {
229
- const tx = screenX - viewport.x;
230
- const ty = screenY - viewport.y;
231
- const cos = Math.cos(viewport.rotation);
232
- const sin = Math.sin(viewport.rotation);
233
229
  return {
234
- x: (tx * cos + ty * sin) / viewport.zoom,
235
- y: (-tx * sin + ty * cos) / viewport.zoom
230
+ x: (screenX - viewport.x) / viewport.zoom,
231
+ y: (screenY - viewport.y) / viewport.zoom
236
232
  };
237
233
  }
238
234
  function pageToScreen(viewport, pageX, pageY) {
239
- const scaledX = pageX * viewport.zoom;
240
- const scaledY = pageY * viewport.zoom;
241
- const cos = Math.cos(viewport.rotation);
242
- const sin = Math.sin(viewport.rotation);
243
235
  return {
244
- x: scaledX * cos - scaledY * sin + viewport.x,
245
- y: scaledX * sin + scaledY * cos + viewport.y
236
+ x: pageX * viewport.zoom + viewport.x,
237
+ y: pageY * viewport.zoom + viewport.y
246
238
  };
247
239
  }
248
240
  function setViewport(viewport, updater) {
249
241
  return {
250
242
  x: updater.x ?? viewport.x,
251
243
  y: updater.y ?? viewport.y,
252
- zoom: updater.zoom ?? viewport.zoom,
253
- rotation: updater.rotation ?? viewport.rotation
244
+ zoom: updater.zoom ?? viewport.zoom
254
245
  };
255
246
  }
256
247
  function panViewport(viewport, dx, dy) {
@@ -262,23 +253,92 @@ function zoomViewport(viewport, factor, centerX, centerY) {
262
253
  return { ...viewport, zoom };
263
254
  }
264
255
  const pageBefore = screenToPage(viewport, centerX, centerY);
265
- const cos = Math.cos(viewport.rotation);
266
- const sin = Math.sin(viewport.rotation);
267
- const x = centerX - (pageBefore.x * zoom * cos - pageBefore.y * zoom * sin);
268
- const y = centerY - (pageBefore.x * zoom * sin + pageBefore.y * zoom * cos);
269
- return { x, y, zoom, rotation: viewport.rotation };
270
- }
271
- function rotateViewport(viewport, delta, centerX, centerY) {
272
- const rotation = viewport.rotation + delta;
273
- if (centerX == null || centerY == null) {
274
- return { ...viewport, rotation };
256
+ const x = centerX - pageBefore.x * zoom;
257
+ const y = centerY - pageBefore.y * zoom;
258
+ return { x, y, zoom };
259
+ }
260
+
261
+ // src/canvas/cameraPan.ts
262
+ var VELOCITY_LERP_FACTOR = 0.5;
263
+ var VELOCITY_ZERO_THRESHOLD = 0.01;
264
+ function beginCameraPan(viewport, screenX, screenY) {
265
+ return {
266
+ initialViewportX: viewport.x,
267
+ initialViewportY: viewport.y,
268
+ originScreenX: screenX,
269
+ originScreenY: screenY,
270
+ velocityX: 0,
271
+ velocityY: 0,
272
+ previousScreenX: screenX,
273
+ previousScreenY: screenY,
274
+ lastMoveTime: performance.now()
275
+ };
276
+ }
277
+ function moveCameraPan(session, currentScreenX, currentScreenY) {
278
+ const now = performance.now();
279
+ const elapsed = now - session.lastMoveTime;
280
+ if (elapsed > 0) {
281
+ const moveDx = currentScreenX - session.previousScreenX;
282
+ const moveDy = currentScreenY - session.previousScreenY;
283
+ const moveLen = Math.hypot(moveDx, moveDy);
284
+ if (moveLen > 0) {
285
+ const dirX = moveDx / moveLen;
286
+ const dirY = moveDy / moveLen;
287
+ const speed = moveLen / elapsed;
288
+ session.velocityX += (dirX * speed - session.velocityX) * VELOCITY_LERP_FACTOR;
289
+ session.velocityY += (dirY * speed - session.velocityY) * VELOCITY_LERP_FACTOR;
290
+ }
291
+ if (Math.abs(session.velocityX) < VELOCITY_ZERO_THRESHOLD) session.velocityX = 0;
292
+ if (Math.abs(session.velocityY) < VELOCITY_ZERO_THRESHOLD) session.velocityY = 0;
275
293
  }
276
- const pageBefore = screenToPage(viewport, centerX, centerY);
277
- const cos = Math.cos(rotation);
278
- const sin = Math.sin(rotation);
279
- const x = centerX - (pageBefore.x * viewport.zoom * cos - pageBefore.y * viewport.zoom * sin);
280
- const y = centerY - (pageBefore.x * viewport.zoom * sin + pageBefore.y * viewport.zoom * cos);
281
- return { x, y, zoom: viewport.zoom, rotation };
294
+ session.previousScreenX = currentScreenX;
295
+ session.previousScreenY = currentScreenY;
296
+ session.lastMoveTime = now;
297
+ return {
298
+ x: session.initialViewportX + (currentScreenX - session.originScreenX),
299
+ y: session.initialViewportY + (currentScreenY - session.originScreenY)
300
+ };
301
+ }
302
+ var SLIDE_FRICTION = 0.92;
303
+ var SLIDE_MIN_SPEED = 0.01;
304
+ var SLIDE_MAX_SPEED = 2;
305
+ var SLIDE_MIN_VELOCITY_TO_START = 0.1;
306
+ function startCameraSlide(session, applyPan, onFrame) {
307
+ const timeSinceLastMove = performance.now() - session.lastMoveTime;
308
+ const FRAME_DURATION = 16;
309
+ const decayFactor = Math.pow(1 - VELOCITY_LERP_FACTOR, timeSinceLastMove / FRAME_DURATION);
310
+ const effectiveVx = session.velocityX * decayFactor;
311
+ const effectiveVy = session.velocityY * decayFactor;
312
+ const speed = Math.hypot(effectiveVx, effectiveVy);
313
+ const clampedSpeed = Math.min(speed, SLIDE_MAX_SPEED);
314
+ if (clampedSpeed < SLIDE_MIN_VELOCITY_TO_START) return null;
315
+ const dirX = effectiveVx / speed;
316
+ const dirY = effectiveVy / speed;
317
+ let currentSpeed = clampedSpeed;
318
+ let lastTime = performance.now();
319
+ let rafId = 0;
320
+ const tick = () => {
321
+ const now = performance.now();
322
+ const elapsed = now - lastTime;
323
+ lastTime = now;
324
+ applyPan(dirX * currentSpeed * elapsed, dirY * currentSpeed * elapsed);
325
+ onFrame();
326
+ currentSpeed *= SLIDE_FRICTION;
327
+ if (currentSpeed < SLIDE_MIN_SPEED) {
328
+ rafId = 0;
329
+ return;
330
+ }
331
+ rafId = requestAnimationFrame(tick);
332
+ };
333
+ rafId = requestAnimationFrame(tick);
334
+ return {
335
+ stop() {
336
+ if (rafId !== 0) {
337
+ cancelAnimationFrame(rafId);
338
+ rafId = 0;
339
+ }
340
+ }
341
+ };
282
342
  }
283
343
 
284
344
  // src/utils/colors.ts
@@ -311,7 +371,6 @@ var CanvasRenderer = class {
311
371
  render(ctx, viewport, shapes) {
312
372
  ctx.save();
313
373
  ctx.translate(viewport.x, viewport.y);
314
- ctx.rotate(viewport.rotation);
315
374
  ctx.scale(viewport.zoom, viewport.zoom);
316
375
  for (const shape of shapes) {
317
376
  if (shape.type === "draw") {
@@ -804,7 +863,7 @@ var PenDrawingState = class extends StateNode {
804
863
  const z = this._startInfo?.point?.z ?? 0.5;
805
864
  this._isPenDevice = penActive;
806
865
  this._hasPressure = penActive || z !== 0.5;
807
- const pressure = this._hasPressure ? toFixed(z) : 0.5;
866
+ const pressure = this._hasPressure ? toFixed(z * 1.25) : 0.5;
808
867
  this._phase = inputs.getShiftKey() ? "straight" : "free";
809
868
  this._extending = false;
810
869
  this._lastSample = { ...origin };
@@ -901,13 +960,12 @@ var PenDrawingState = class extends StateNode {
901
960
  const curPt = inputs.getCurrentPagePoint();
902
961
  if (!this._hasPressure) {
903
962
  const liveZ = curPt.z ?? 0.5;
904
- if (liveZ !== 0.5 || inputs.getIsPen()) {
963
+ if (liveZ > 0 && liveZ !== 0.5 || inputs.getIsPen()) {
905
964
  this._hasPressure = true;
906
- this.editor.updateShapes([{ id, type: "draw", props: { isPen: true } }]);
907
965
  }
908
966
  }
909
967
  const local = this.editor.getPointInShapeSpace(shape, curPt);
910
- const pressure = this._hasPressure ? toFixed(curPt.z ?? 0.5) : 0.5;
968
+ const pressure = this._hasPressure ? toFixed((curPt.z ?? 0.5) * 1.25) : 0.5;
911
969
  const pt = { x: toFixed(local.x), y: toFixed(local.y), z: pressure };
912
970
  switch (this._phase) {
913
971
  case "starting_straight": {
@@ -1053,7 +1111,7 @@ var PenDrawingState = class extends StateNode {
1053
1111
  const firstPt = {
1054
1112
  x: 0,
1055
1113
  y: 0,
1056
- z: this._hasPressure ? toFixed(curPage.z ?? 0.5) : 0.5
1114
+ z: this._hasPressure ? toFixed((curPage.z ?? 0.5) * 1.25) : 0.5
1057
1115
  };
1058
1116
  this._activePts = [firstPt];
1059
1117
  this.editor.createShape({
@@ -1085,7 +1143,7 @@ var PenDrawingState = class extends StateNode {
1085
1143
  endStroke() {
1086
1144
  if (!this._target) return;
1087
1145
  this.editor.updateShapes([
1088
- { id: this._target.id, type: "draw", props: { isComplete: true } }
1146
+ { id: this._target.id, type: "draw", props: { isComplete: true, isPen: this._hasPressure } }
1089
1147
  ]);
1090
1148
  this.ctx.transition("pen_idle");
1091
1149
  }
@@ -1515,16 +1573,29 @@ var HandIdleState = class extends StateNode {
1515
1573
  // src/tools/hand/states/HandDraggingState.ts
1516
1574
  var HandDraggingState = class extends StateNode {
1517
1575
  static id = "hand_dragging";
1576
+ panSession = null;
1577
+ onEnter(info) {
1578
+ const downInfo = info;
1579
+ const screenX = downInfo?.screenX ?? 0;
1580
+ const screenY = downInfo?.screenY ?? 0;
1581
+ this.panSession = beginCameraPan(this.editor.viewport, screenX, screenY);
1582
+ }
1518
1583
  onPointerMove(info) {
1519
- const move = info ?? {};
1520
- const dx = move.screenDeltaX ?? 0;
1521
- const dy = move.screenDeltaY ?? 0;
1522
- if (dx === 0 && dy === 0) return;
1523
- this.editor.panBy(dx, dy);
1584
+ if (!this.panSession) return;
1585
+ const screenX = info?.screenX ?? 0;
1586
+ const screenY = info?.screenY ?? 0;
1587
+ const target = moveCameraPan(this.panSession, screenX, screenY);
1588
+ this.editor.setViewport({ x: target.x, y: target.y });
1589
+ }
1590
+ getPanSession() {
1591
+ return this.panSession;
1524
1592
  }
1525
1593
  onPointerUp() {
1526
1594
  this.ctx.transition("hand_idle");
1527
1595
  }
1596
+ onExit() {
1597
+ this.panSession = null;
1598
+ }
1528
1599
  onCancel() {
1529
1600
  this.ctx.transition("hand_idle");
1530
1601
  }
@@ -1752,8 +1823,7 @@ var Editor = class {
1752
1823
  this.viewport = {
1753
1824
  x: partial.x ?? this.viewport.x,
1754
1825
  y: partial.y ?? this.viewport.y,
1755
- zoom: Math.max(0.1, Math.min(4, rawZoom)),
1756
- rotation: partial.rotation ?? this.viewport.rotation
1826
+ zoom: Math.max(0.1, Math.min(4, rawZoom))
1757
1827
  };
1758
1828
  this.emitChange();
1759
1829
  }
@@ -1767,10 +1837,6 @@ var Editor = class {
1767
1837
  this.viewport = zoomViewport(this.viewport, factor, screenX, screenY);
1768
1838
  this.emitChange();
1769
1839
  }
1770
- rotateAt(delta, screenX, screenY) {
1771
- this.viewport = rotateViewport(this.viewport, delta, screenX, screenY);
1772
- this.emitChange();
1773
- }
1774
1840
  deleteShapes(ids) {
1775
1841
  if (ids.length === 0) return;
1776
1842
  this.store.deleteShapes(ids);
@@ -1793,8 +1859,7 @@ var Editor = class {
1793
1859
  viewport: {
1794
1860
  x: this.viewport.x,
1795
1861
  y: this.viewport.y,
1796
- zoom: this.viewport.zoom,
1797
- rotation: this.viewport.rotation
1862
+ zoom: this.viewport.zoom
1798
1863
  },
1799
1864
  currentToolId: this.getCurrentToolId(),
1800
1865
  drawStyle: this.getCurrentDrawStyle(),
@@ -1802,10 +1867,7 @@ var Editor = class {
1802
1867
  };
1803
1868
  }
1804
1869
  loadSessionStateSnapshot(snapshot) {
1805
- this.setViewport({
1806
- ...snapshot.viewport,
1807
- rotation: snapshot.viewport.rotation ?? 0
1808
- });
1870
+ this.setViewport(snapshot.viewport);
1809
1871
  this.setCurrentDrawStyle({
1810
1872
  color: snapshot.drawStyle.color,
1811
1873
  dash: snapshot.drawStyle.dash,
@@ -2192,6 +2254,6 @@ function applyResize(editor, handle, startBounds, startShapes, pointer, lockAspe
2192
2254
  }
2193
2255
  }
2194
2256
 
2195
- export { CanvasRenderer, CircleDrawingState, CircleIdleState, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, DocumentStore, ERASER_MARGIN, Editor, EraserErasingState, EraserIdleState, EraserPointingState, HandDraggingState, HandIdleState, InputManager, MAX_POINTS_PER_SHAPE, PenDrawingState, PenIdleState, STROKE_WIDTHS, SelectIdleState, SquareDrawingState, SquareIdleState, StateNode, ToolManager, applyMove, applyResize, applyRotation, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds2 as getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, rotateViewport, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, zoomViewport };
2257
+ export { CanvasRenderer, CircleDrawingState, CircleIdleState, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, DocumentStore, ERASER_MARGIN, Editor, EraserErasingState, EraserIdleState, EraserPointingState, HandDraggingState, HandIdleState, InputManager, MAX_POINTS_PER_SHAPE, PenDrawingState, PenIdleState, STROKE_WIDTHS, SelectIdleState, SquareDrawingState, SquareIdleState, StateNode, ToolManager, applyMove, applyResize, applyRotation, beginCameraPan, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds2 as getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, moveCameraPan, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, startCameraSlide, zoomViewport };
2196
2258
  //# sourceMappingURL=index.js.map
2197
2259
  //# sourceMappingURL=index.js.map