1+ import { WIDTH , HEIGHT } from '../core/constants.js'
2+ import { api as entityModule } from '../entity-module/GraphicEntityModule.js'
3+ import { easeOut } from '../core/transitions.js'
4+ import { lerpPosition } from '../core/utils.js'
5+
6+ export class CameraModule {
7+ constructor ( assets ) {
8+ CameraModule . instance = this
9+ this . container = { id : - 1 , sizeX : - 1 , sizeY : - 1 }
10+ this . cameraOffset = 0
11+ this . previousFrame = {
12+ registered : new Map ( )
13+ }
14+ this . lastFrame = - 1
15+ this . cameraEndPosition = { x : 0 , y : 0 }
16+ this . cameraEndScale = 1
17+ this . cameraCurve = t => t
18+ this . oldZoomState = { position : { x : 0 , y : 0 } , boundSize : { x : 0 , y : 0 } }
19+ this . oldCameraState = { scale : { x : - 1 , y : - 1 } , position : { x : 0 , y : 0 } }
20+ this . currentCameraState = { scale : { x : - 1 , y : - 1 } , position : { x : 0 , y : 0 } }
21+ this . previousUpdateData = this . currentUpdateFrame = this . currentUpdateProgress = undefined
22+ this . viewerActive = true
23+ this . active = true
24+
25+ }
26+
27+ static get name ( ) {
28+ return 'c'
29+ }
30+
31+ setActive ( active ) {
32+ this . viewerActive = active
33+ if ( this . currentUpdateProgress !== undefined ) {
34+ this . lastFrame = - 1
35+ this . updateScene ( this . previousUpdateData , this . currentUpdateFrame , this . currentUpdateProgress )
36+ }
37+ }
38+
39+ getRelativePosFromContainer ( entity , containerId ) {
40+ let x = 0
41+ let y = 0
42+ let root = entity
43+ let debug = 0
44+ while ( root . parent !== null && root . id !== containerId ) {
45+ x += root . currentState . x
46+ y += root . currentState . y
47+ root = root . parent
48+ debug ++
49+ if ( debug > 10 ) {
50+ throw new Error ( "this is too long" )
51+ }
52+ }
53+ return { x, y}
54+ }
55+
56+ updateScene ( previousData , currentData , progress ) {
57+ const isActive = ( currentData . registered . size !== 0 ) && ( currentData . container . entity !== null )
58+ if ( ! ( currentData . active && this . viewerActive ) ) {
59+ if ( isActive ) {
60+ currentData . container . entity . graphics . scale = { x : 1 , y : 1 }
61+ currentData . container . entity . graphics . position = { x : 0 , y : 0 }
62+ }
63+ return
64+ }
65+ this . currentUpdateFrame = currentData
66+ this . currentUpdateProgress = progress
67+ this . previousUpdateData = previousData
68+ if ( this . lastFrame !== currentData . number ) {
69+ this . lastFrame = currentData . number
70+ if ( isActive ) {
71+ this . oldCameraState = { ...this . currentCameraState }
72+ let maxX , minX , minY , maxY ;
73+ let first = true ;
74+ entityModule . entities . forEach (
75+ entity => {
76+
77+ if ( currentData . registered . get ( entity . id + "" ) ) {
78+ //console.log(`added entity ${entity.id} which is at x = ${entity.currentState.x}, y = ${entity.currentState.y}`)
79+ const relativePos = this . getRelativePosFromContainer ( entity , currentData . container . entity . id )
80+ if ( first ) {
81+ minX = maxX = relativePos . x
82+ minY = maxY = relativePos . y
83+ first = false
84+ } else {
85+ minX = Math . min ( minX , relativePos . x )
86+ minY = Math . min ( minY , relativePos . y )
87+ maxX = Math . max ( maxX , relativePos . x )
88+ maxY = Math . max ( maxY , relativePos . y )
89+ }
90+
91+ }
92+ }
93+ )
94+ const averagePoint = { x : ( maxX + minX ) / 2 , y : ( maxY + minY ) / 2 }
95+ const boundSize = { x : maxX - minX , y : maxY - minY }
96+ // if (this.oldCameraState.scale.x !== -1 && progress !== 1) {
97+ // currentData.container.entity.graphics.position = this.oldCameraState.position
98+ // currentData.container.entity.graphics.scale = this.oldCameraState.scale
99+ //
100+ // }
101+ const containerState = currentData . container . entity . currentState
102+ const scale2 = Math . min ( HEIGHT / ( boundSize . y + currentData . cameraOffset ) , WIDTH / ( boundSize . x + currentData . cameraOffset ) )
103+ const scale = { x : scale2 / containerState . scaleX , y : scale2 / containerState . scaleY }
104+ //const scale = 1
105+ this . cameraEndScale = scale
106+
107+ // if position is not relative del container.entity.x
108+ const newX = ( ( currentData . container . sizeX / 2 - averagePoint . x ) * scale2
109+ - ( scale2 - 1 ) * currentData . container . sizeX / 2
110+ + ( WIDTH / 2 - ( containerState . x + currentData . container . sizeX / 2 ) ) ) / containerState . scaleX
111+
112+ const newY = ( ( currentData . container . sizeY / 2 - averagePoint . y ) * scale2
113+ - ( scale2 - 1 ) * currentData . container . sizeY / 2
114+ + ( HEIGHT / 2 - ( containerState . y + currentData . container . sizeY / 2 ) ) ) / containerState . scaleY
115+
116+ // currentData.container.entity.graphics.scale.x = currentData.container.entity.graphics.scale.y = 0.5
117+ this . cameraEndPosition = { x : newX , y : newY }
118+ //console.log(`frame ${currentData.number}, ${Math.round(progress*100)/100}%,container to x : ${newX}, y : ${newY}, scale : ${scale}`)
119+ const position = averagePoint
120+ this . cameraCurve = ( position . x - this . oldZoomState . position . x ) ** 2 +
121+ ( position . y - this . oldZoomState . position . y ) ** 2 >= currentData . cameraOffset ** 2
122+ || Math . max ( Math . abs ( boundSize . x - this . oldZoomState . boundSize . x ) ,
123+ Math . abs ( boundSize . y - this . oldZoomState . boundSize . y ) ) > currentData . cameraOffset ? easeOut : t => t
124+ this . oldZoomState = { boundSize, position}
125+ }
126+
127+ }
128+ if ( ( this . lastFrame === currentData . number || progress === 1 ) && isActive ) {
129+ const currentPoint = lerpPosition ( this . oldCameraState . position , this . cameraEndPosition , this . cameraCurve ( progress ) )
130+ currentData . container . entity . graphics . position = currentPoint
131+ const currentScale = lerpPosition ( this . oldCameraState . scale , this . cameraEndScale , this . cameraCurve ( progress ) )
132+ currentData . container . entity . graphics . scale = currentScale
133+ console . log ( `frame ${ currentData . number } , ${ Math . round ( progress * 100 ) / 100 } %,container to x : ${ currentPoint . x } , y : ${ currentPoint . y } , scale : ${ currentScale . x } ` )
134+ this . currentCameraState = { scale : currentScale , position : currentPoint }
135+
136+ }
137+ }
138+
139+ handleFrameData ( frameInfo , data ) {
140+ if ( data === undefined ) {
141+ const registered = new Map ( this . previousFrame . registered )
142+ const cameraOffset = this . cameraOffset
143+ const container = this . container . id !== - 1 ? {
144+ entity : entityModule . entities . get ( this . container . id ) ,
145+ sizeX : this . container . sizeX , sizeY : this . container . sizeY
146+ } : null
147+ const active = this . active
148+ const frame = { registered, number : frameInfo . number , cameraOffset, container, active}
149+ this . previousFrame = frame
150+ return frame
151+ }
152+ // const newRegistration = data[0] === undefined ? new Map() : data[0]
153+ const newRegistration = data [ 0 ] || new Map ( )
154+ const registered = new Map ( this . previousFrame . registered )
155+ Object . keys ( newRegistration ) . forEach (
156+ ( k ) => {
157+ registered . set ( k , newRegistration [ k ] )
158+ }
159+ )
160+ this . cameraOffset = data [ 1 ] || this . cameraOffset
161+ this . container = data [ 2 ] ? { id : data [ 2 ] [ 0 ] , sizeX : data [ 2 ] [ 1 ] , sizeY : data [ 2 ] [ 2 ] } : this . container
162+
163+ const active = data [ 3 ] === null ? this . active : data [ 3 ]
164+ this . active = active
165+ const cameraOffset = this . cameraOffset
166+ const container = this . container . id !== - 1 ? {
167+ entity : entityModule . entities . get ( this . container . id ) ,
168+ sizeX : this . container . sizeX , sizeY : this . container . sizeY
169+ } : null
170+ const frame = { registered, number : frameInfo . number , cameraOffset, container, active}
171+ this . previousFrame = frame
172+ return frame
173+ }
174+
175+ reinitScene ( ) {
176+
177+ }
178+
179+ }
0 commit comments