
view.camera-module.CameraModule.js Maven / Gradle / Ivy
import {WIDTH, HEIGHT} from '../core/constants.js'
import {api as entityModule} from '../entity-module/GraphicEntityModule.js'
import {easeOut} from '../core/transitions.js'
import {lerpPosition} from '../core/utils.js'
export class CameraModule {
constructor(_assets) {
CameraModule.instance = this
this.container = {id: -1, sizeX: -1, sizeY: -1}
this.cameraOffset = 0
this.previousFrame = {
registered: new Map()
}
this.lastFrame = -1
this.cameraEndPosition = {x: 0, y: 0}
this.cameraEndScale = 1
this.cameraCurve = t => t
this.oldZoomState = {position: {x: 0, y: 0}, boundSize: {x: 0, y: 0}}
this.oldCameraState = {scale: {x: -1, y: -1}, position: {x: 0, y: 0}}
this.currentCameraState = {scale: {x: -1, y: -1}, position: {x: 0, y: 0}}
this.previousUpdateData = this.currentUpdateFrame = this.currentUpdateProgress = undefined
this.active = true
}
static get name() {
return 'c'
}
static setActive(active) {
CameraModule.viewerActive = active
const module = CameraModule.instance
module.lastFrame = -2
if (module.previousUpdateData !== undefined) {
module.updateScene(module.previousUpdateData, module.currentUpdateFrame, module.currentUpdateProgress || 1)
}
}
getRelativePosFromContainer(entity, containerId) {
let x = 0
let y = 0
let root = entity
let debug = 0
while (root.parent !== null && root.id !== containerId) {
x += root.currentState.x
y += root.currentState.y
root = root.parent
debug++
if (debug > 500) {
throw new Error("this is too long") // break point to be sure the program doesn't
// loop infinitely, should never be triggered
}
}
return {x, y}
}
updateScene(previousData, currentData, progress) {
this.currentUpdateFrame = currentData
this.currentUpdateProgress = progress
this.previousUpdateData = previousData
const isActive = (currentData.registered.size !== 0) && (currentData.container.entity !== null)
if (!(currentData.active && CameraModule.viewerActive)) {
if (isActive) {
currentData.container.entity.graphics.scale = {x: 1, y: 1}
currentData.container.entity.graphics.position = {x: 0, y: 0}
}
return
}
if (!isActive) {
const boundSize = {x: 1920, y: 1080}
const position = {x: 0, y: 0}
this.oldCameraState = {boundSize, position}
}
if (this.lastFrame !== currentData.number && isActive) {
this.oldCameraState = {...this.currentCameraState}
let maxX, minX, minY, maxY;
let first = true;
entityModule.entities.forEach(
entity => {
if (currentData.registered.get(entity.id + "")) {
const relativePos = this.getRelativePosFromContainer(entity, currentData.container.entity.id)
if (first) {
minX = maxX = relativePos.x
minY = maxY = relativePos.y
first = false
} else {
minX = Math.min(minX, relativePos.x)
minY = Math.min(minY, relativePos.y)
maxX = Math.max(maxX, relativePos.x)
maxY = Math.max(maxY, relativePos.y)
}
}
}
)
const averagePoint = {x: (maxX + minX) / 2, y: (maxY + minY) / 2}
const boundSize = {x: maxX - minX, y: maxY - minY}
const containerState = currentData.container.entity.currentState
const scale2 = Math.min(HEIGHT / (boundSize.y + currentData.cameraOffset), WIDTH / (boundSize.x + currentData.cameraOffset))
const scale = {x: scale2 / containerState.scaleX, y: scale2 / containerState.scaleY}
this.cameraEndScale = scale
const newX = ((currentData.container.sizeX / 2 - averagePoint.x) * scale2
- (scale2 - 1) * currentData.container.sizeX / 2
+ (WIDTH / 2 - (containerState.x + currentData.container.sizeX / 2))) / containerState.scaleX
const newY = ((currentData.container.sizeY / 2 - averagePoint.y) * scale2
- (scale2 - 1) * currentData.container.sizeY / 2
+ (HEIGHT / 2 - (containerState.y + currentData.container.sizeY / 2))) / containerState.scaleY
// currentData.container.entity.graphics.scale.x = currentData.container.entity.graphics.scale.y = 0.5
this.cameraEndPosition = {x: newX, y: newY}
//console.log(`frame ${currentData.number}, ${Math.round(progress*100)/100}%,container to x : ${newX}, y : ${newY}, scale : ${scale}`)
const position = averagePoint
this.cameraCurve = (position.x - this.oldZoomState.position.x) ** 2 +
(position.y - this.oldZoomState.position.y) ** 2 >= currentData.cameraOffset ** 2
|| Math.max(Math.abs(boundSize.x - this.oldZoomState.boundSize.x),
Math.abs(boundSize.y - this.oldZoomState.boundSize.y)) > currentData.cameraOffset ? easeOut : t => t
this.oldZoomState = {boundSize, position}
}
const realProgress = Math.abs(currentData.number - this.lastFrame) > 1 ? 1 : progress
if (isActive) {
const currentPoint = lerpPosition(this.oldCameraState.position, this.cameraEndPosition, this.cameraCurve(realProgress))
currentData.container.entity.graphics.position = currentPoint
const currentScale = lerpPosition(this.oldCameraState.scale, this.cameraEndScale, this.cameraCurve(realProgress))
currentData.container.entity.graphics.scale = currentScale
this.currentCameraState = {scale: currentScale, position: currentPoint}
}
this.lastFrame = currentData.number
}
handleFrameData(frameInfo, data) {
if (data === undefined) {
const registered = new Map(this.previousFrame.registered)
const cameraOffset = this.cameraOffset
const container = this.container.id !== -1 ? {
entity: entityModule.entities.get(this.container.id),
sizeX: this.container.sizeX, sizeY: this.container.sizeY
} : null
const active = this.active
const frame = {registered, number: frameInfo.number, cameraOffset, container, active}
this.previousFrame = frame
return frame
}
const newRegistration = data[0] || new Map()
const registered = new Map(this.previousFrame.registered)
Object.keys(newRegistration).forEach(
(k) => {
registered.set(k, newRegistration[k])
}
)
this.cameraOffset = data[1] || this.cameraOffset
this.container = data[2] ? {id: data[2][0], sizeX: data[2][1], sizeY: data[2][2]} : this.container
const active = data[3] === null ? this.active : data[3]
this.active = active
const cameraOffset = this.cameraOffset
const container = this.container.id !== -1 ? {
entity: entityModule.entities.get(this.container.id),
sizeX: this.container.sizeX, sizeY: this.container.sizeY
} : null
const frame = {registered, number: frameInfo.number, cameraOffset, container, active}
this.previousFrame = frame
return frame
}
reinitScene() {
}
}
CameraModule.viewerActive = true
© 2015 - 2025 Weber Informatics LLC | Privacy Policy