From 07953ab18d6e71df821776032121fde5b2bd7b3f Mon Sep 17 00:00:00 2001 From: Hiren Kangad Date: Sat, 14 Mar 2026 11:21:08 +0530 Subject: [PATCH] feat: SceneManager creates pdf-page items with culling support --- frontend/src/canvas/PixiCanvas.tsx | 7 ++++-- frontend/src/canvas/SceneManager.ts | 37 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/frontend/src/canvas/PixiCanvas.tsx b/frontend/src/canvas/PixiCanvas.tsx index b8787fa..db0dcad 100644 --- a/frontend/src/canvas/PixiCanvas.tsx +++ b/frontend/src/canvas/PixiCanvas.tsx @@ -22,6 +22,7 @@ import { TextureManager } from './TextureManager'; import { ImageSprite } from './sprites/ImageSprite'; import { VideoSprite } from './sprites/VideoSprite'; import { AnimatedGifSprite } from './sprites/AnimatedGifSprite'; +import { PdfPageSprite } from './sprites/PdfPageSprite'; import { SpringManager } from './spring'; import { convertFabricToV2 } from './scene-format'; import type { SceneData } from './scene-format'; @@ -197,7 +198,7 @@ const PixiCanvas = forwardRef( const vcx = bounds.x + bounds.width / 2; const vcy = bounds.y + bounds.height / 2; - const imageItems: { item: SceneItem; sprite: ImageSprite | AnimatedGifSprite; dist: number; near: boolean }[] = []; + const imageItems: { item: SceneItem; sprite: ImageSprite | AnimatedGifSprite | PdfPageSprite; dist: number; near: boolean }[] = []; const videoItems: { item: SceneItem; sprite: VideoSprite; dist: number; near: boolean }[] = []; // Spatial query: only check items within the extended viewport region @@ -215,6 +216,8 @@ const PixiCanvas = forwardRef( if (item.type === 'image' && (item.displayObject instanceof ImageSprite || item.displayObject instanceof AnimatedGifSprite)) { imageItems.push({ item, sprite: item.displayObject, dist, near: true }); + } else if (item.type === 'pdf-page' && item.displayObject instanceof PdfPageSprite) { + imageItems.push({ item, sprite: item.displayObject, dist, near: true }); } else if (item.type === 'video' && item.displayObject instanceof VideoSprite) { videoItems.push({ item, sprite: item.displayObject, dist, near: true }); } @@ -257,7 +260,7 @@ const PixiCanvas = forwardRef( const item = scene.items.get(id); if (!item) { loadedImages.delete(id); continue; } const sprite = item.displayObject; - if ((sprite instanceof ImageSprite || sprite instanceof AnimatedGifSprite) && sprite.loaded) { + if ((sprite instanceof ImageSprite || sprite instanceof AnimatedGifSprite || sprite instanceof PdfPageSprite) && sprite.loaded) { sprite.unloadTexture(); } loadedImages.delete(id); diff --git a/frontend/src/canvas/SceneManager.ts b/frontend/src/canvas/SceneManager.ts index 09e3c46..7a7a7e2 100644 --- a/frontend/src/canvas/SceneManager.ts +++ b/frontend/src/canvas/SceneManager.ts @@ -15,6 +15,7 @@ import { DrawingSprite } from './sprites/DrawingSprite'; import { FrameSprite } from './sprites/FrameSprite'; import { StickySprite } from './sprites/StickySprite'; import { MarkdownSprite } from './sprites/MarkdownSprite'; +import { PdfPageSprite } from './sprites/PdfPageSprite'; import { TextSprite } from './sprites/TextSprite'; import { SpringManager, Spring, PRESETS } from './spring'; import { reparentGroupChildren } from './grouping'; @@ -38,6 +39,7 @@ import type { GroupObject, StickyObject, MarkdownObject, + PdfPageObject, } from './scene-format'; // --------------------------------------------------------------------------- @@ -374,6 +376,13 @@ export class SceneManager { break; } + case 'pdf-page': { + const d = data as PdfPageObject; + const sprite = new PdfPageSprite(d.thumb, d.asset, d.w, d.h, d.pageNumber, d.pageCount, this.textures); + displayObject = sprite; + break; + } + default: displayObject = new Container(); break; @@ -710,6 +719,34 @@ export class SceneManager { return this.items.get(data.id)!; } + /** Create a PdfPageObject from a PDF upload and add it to the scene. */ + addPdfPageFromUpload( + thumbKey: string | null, assetKey: string | null, + w: number, h: number, x: number, y: number, + pdfImageId: string, pdfName: string, + pageNumber: number, pageCount: number, + ): SceneItem { + const data: PdfPageObject = { + id: crypto.randomUUID(), + type: 'pdf-page', + x, y, w, h, + sx: 1, sy: 1, angle: 0, + z: this.nextZ(), + opacity: 1, + locked: false, + visible: true, + name: `${pdfName} p.${pageNumber}`, + asset: assetKey, + thumb: thumbKey, + pdfImageId, pdfName, pageNumber, pageCount, + nativeW: w, nativeH: h, + }; + this._createItem(data); + this._applyZOrder(); + this._onChange?.(); + return this.items.get(data.id)!; + } + // -- Group / Ungroup with Spring Animation -------------------------------- /**