import { ArtTileManager } from "./ArtTileManager.js"; import { HelperFunctions } from "./HelperFunctions.js"; import ImageVideoPopout from "./MultiMediaPopout.js"; /** * This class manages the images specifically, setting and clearing the tiles' images */ export class ImageDisplayManager { static async getTilesFromArtScene() { let artSceneID = await game.JTCS.utils.getSettingValue( "artGallerySettings", "dedicatedDisplayData.scene.value" ); let ourScene = game.scenes.get(artSceneID); let artTiles = await game.JTCS.tileUtils.getSceneSlideshowTiles("art", true, { currentSceneID: artSceneID, }); let artTileID = artTiles[0].id; let frameTiles = await game.JTCS.tileUtils.getSceneSlideshowTiles("frame", true, { currentSceneID: artSceneID, }); let frameTileID = artTiles[0].linkedBoundingTile || frameTiles[0].id; return { ourScene: ourScene, artTileID: artTileID, frameTileID: frameTileID, }; } static async updateTileObjectTexture( artTileID, frameTileID, url, method, sceneID = "" ) { let ourScene = game.scenes.get(sceneID); if (!ourScene) ourScene = game.scenes.viewed; let artTile = ourScene.tiles.get(artTileID); let frameTile = ourScene.tiles.get(frameTileID); //load the texture from the source if (!artTile || !url) { ui.notifications.error("Tile or image not found"); console.error(url, artTile, artTileID); return; } const tex = await loadTexture(url); if (!tex) { ui.notifications.error( `Error loading texture from '${url}'. Access to URL likely blocked by CORS policy.` ); return; } let imageUpdate; if (!frameTile) { imageUpdate = await ImageDisplayManager.scaleArtTileToScene( artTile, tex, url, sceneID ); } else { imageUpdate = await ImageDisplayManager.scaleArtTileToFrameTile( artTile, frameTile, tex, url, sceneID ); } const updated = await ourScene .updateEmbeddedDocuments("Tile", [imageUpdate]) .catch((error) => ui.notifications.error( `Default art tile in ${ourScene.name} couldn't be updated` ) ); if (updated && method === "artScene") { const { autoActivate, autoView } = await HelperFunctions.getSettingValue( "artGallerySettings", "dedicatedDisplayData.scene" ); if (autoActivate) { ourScene.activate(); } if (autoView) { ourScene.view(); } if ( game.user.isGM && //if we're GM ((!autoActivate && !autoView) || (ourScene.active && !ourScene.viewed)) //if the scene is neither set to activate or to view, notify that the image updated. // if the scene is active but not viewed, notify that the image updated ) { ui.notifications.info( `Default Tile in Art Scene '${ourScene.name}' successfully updated` ); } } } static async scaleArtTileToScene(displayTile, tex, url, sceneID = "") { let displayScene = game.scenes.get(sceneID); if (!displayScene) displayScene = game.scenes.viewed; let displaySceneWidth = game.version >= 10 ? displayScene.width : displayScene.data.width; let displaySceneHeight = game.version >= 10 ? displayScene.height : displayScene.data.height; let dimensionObject = await ImageDisplayManager.calculateAspectRatioFit( tex.width, tex.height, displaySceneWidth, displaySceneHeight // displayScene.data.width, // displayScene.data.height ); //scale down factor is how big the tile will be in the scene //make this scale down factor configurable at some point let scaleDownFactor = 200; dimensionObject.width -= scaleDownFactor; dimensionObject.height -= scaleDownFactor; //half of the scene's width or height is the center -- we're subtracting by half of the image's width or height to account for the offset because it's measuring from top/left instead of center //separate objects depending on the texture's dimensions -- //create an 'update' object for if the image is wide (width is bigger than height) let wideImageUpdate = { _id: displayTile.id, width: dimensionObject.width, height: dimensionObject.height, img: url, x: scaleDownFactor / 2, y: displaySceneHeight / 2 - dimensionObject.height / 2, }; //create an 'update' object for if the image is tall (height is bigger than width) let tallImageUpdate = { _id: displayTile.id, width: dimensionObject.width, height: dimensionObject.height, img: url, y: scaleDownFactor / 2, x: displaySceneWidth / 2 - dimensionObject.width / 2, }; //https://stackoverflow.com/questions/38675447/how-do-i-get-the-center-of-an-image-in-javascript //^used the above StackOverflow post to help me figure that out //Determine if the image or video is wide, tall, or same dimensions and update depending on that if (dimensionObject.height > dimensionObject.width) { //if the height is longer than the width, use the tall image object return tallImageUpdate; } else if (dimensionObject.width > dimensionObject.height) { //if the width is longer than the height, use the wide image object return wideImageUpdate; } //if the image length and width are pretty much the same, just default to the wide image update object return wideImageUpdate; } static async scaleArtTileToFrameTile(artTile, frameTile, tex, url) { const frameTileWidth = game.version >= 10 ? frameTile.width : frameTile.data.width; const frameTileHeight = game.version >= 10 ? frameTile.height : frameTile.data.height; const frameTileX = game.version >= 10 ? frameTile.x : frameTile.data.x; const frameTileY = game.version >= 10 ? frameTile.y : frameTile.data.y; let dimensionObject = ImageDisplayManager.calculateAspectRatioFit( tex.width, tex.height, frameTileWidth, frameTileHeight ); let imageUpdate = { _id: artTile.id, width: dimensionObject.width, height: dimensionObject.height, img: url, y: frameTileY, x: frameTileX, }; //Ensure image is centered to bounding tile (stops images hugging the top left corner of the bounding box). let boundingMiddle = { x: frameTileX + frameTileWidth / 2, y: frameTileY + frameTileHeight / 2, }; let imageMiddle = { x: imageUpdate.x + imageUpdate.width / 2, y: imageUpdate.y + imageUpdate.height / 2, }; imageUpdate.x += boundingMiddle.x - imageMiddle.x; imageUpdate.y += boundingMiddle.y - imageMiddle.y; return imageUpdate; } /** Used snippet from the below stackOverflow answer to help me with proportionally resizing the images*/ /*https://stackoverflow.com/questions/3971841/how-to-resize-images-proportionally-keeping-the-aspect-ratio*/ static calculateAspectRatioFit(srcWidth, srcHeight, maxWidth, maxHeight) { let ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight); return { width: srcWidth * ratio, height: srcHeight * ratio, }; } static async displayImageInWindow(method, url) { if (method === "journalEntry") { let dedicatedDisplayData = await HelperFunctions.getSettingValue( "artGallerySettings", "dedicatedDisplayData" ); const displayJournalID = dedicatedDisplayData.journal.value; const displayJournal = game.journal.get(displayJournalID); //if we would like to display in a dedicated journal entry if (!displayJournal) { //couldn't find display journal, so return if (game.user.isGM) { ui.notifications.error( `No Art Journal entry set! Please set your art journal in the module settings or Art Gallery Config` ); } return; } else { displayJournal.render(true); } //get the file type of the image url via regex match var fileTypePattern = /\.[0-9a-z]+$/i; var fileType = url.match(fileTypePattern); let journalMode = "image"; let update; if (fileType == ".mp4" || fileType == ".webm") { if (game.version < 10) { // if the file type is a video and we're before v10, we have to do a bit of a wonky workaround let videoHTML = `