@c4h/chuci 0.1.0 → 0.2.1

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.
@@ -1,381 +1,381 @@
1
- import { CcViewerBase } from './cc-viewer-base'
2
-
3
- export class CcViewerGaussian extends CcViewerBase {
4
- private splatUrl = ''
5
- private debugMode = false
6
- private cameraPosition = '3,3,3'
7
- private _cameraTarget = '0,0,0' // TODO: Implement camera target functionality
8
-
9
- // gsplat.js library types are not available, using any for external library objects
10
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
- private scene?: any
12
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
- private camera?: any
14
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
- private renderer?: any
16
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
- private controls?: any
18
- private animationId?: number
19
- private canvas?: HTMLCanvasElement
20
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
- private swiper?: any
22
-
23
- static get observedAttributes() {
24
- return ['show', 'debug-mode', 'camera-position', 'camera-target']
25
- }
26
-
27
- attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {
28
- if (name === 'show') {
29
- this.isShow = newValue === 'true'
30
- } else if (name === 'debug-mode') {
31
- this.debugMode = newValue === 'true'
32
- } else if (name === 'camera-position') {
33
- this.cameraPosition = newValue || '3,3,3'
34
- } else if (name === 'camera-target') {
35
- this._cameraTarget = newValue || '0,0,0'
36
- }
37
- super.attributeChangedCallback(name, oldValue, newValue)
38
- }
39
-
40
- // Implementation of abstract methods from base class
41
- protected async doOpen(url: string): Promise<void> {
42
- this.splatUrl = url
43
- await this.initializeViewer()
44
- }
45
-
46
- protected doClose(): void {
47
- this.cleanup()
48
- }
49
-
50
- protected getViewerContent(): string {
51
- // This won't be used since we override render, but required by abstract class
52
- return ''
53
- }
54
-
55
- // Override to use custom rendering due to special canvas handling
56
- protected shouldUseCustomRender(): boolean {
57
- return true
58
- }
59
-
60
- // Custom render implementation
61
- protected customRender(): void {
62
- const styles = this.css`
63
- :host {
64
- --cc-viewer-z-index-each: 1000;
65
- }
66
-
67
- .backdrop {
68
- justify-content: center;
69
- align-items: center;
70
- position: fixed;
71
- left: 0;
72
- right: 0;
73
- top: 0;
74
- bottom: 0;
75
- width: 100%;
76
- height: 100%;
77
- background-color: rgba(0, 0, 0, 0.9);
78
- z-index: 1000;
79
- }
80
-
81
- .viewer {
82
- position: absolute;
83
- width: 90%;
84
- height: 85%;
85
- inset: 0px;
86
- margin: auto;
87
- align-self: center;
88
- background-color: #000;
89
- }
90
-
91
- .gaussian-container {
92
- width: 100%;
93
- height: 100%;
94
- position: relative;
95
- background: #000;
96
- }
97
-
98
- .gaussian-container canvas {
99
- width: 100% !important;
100
- height: 100% !important;
101
- display: block;
102
- }
103
-
104
- .loading {
105
- position: absolute;
106
- top: 50%;
107
- left: 50%;
108
- transform: translate(-50%, -50%);
109
- color: #fff;
110
- font-size: 1.2rem;
111
- }
112
-
113
- .error {
114
- position: absolute;
115
- top: 50%;
116
- left: 50%;
117
- transform: translate(-50%, -50%);
118
- color: #e74c3c;
119
- text-align: center;
120
- padding: 20px;
121
- }
122
-
123
- canvas {
124
- display: block;
125
- width: 100%;
126
- height: 100%;
127
- touch-action: none;
128
- }
129
-
130
- .debug-info {
131
- position: absolute;
132
- top: 10px;
133
- left: 10px;
134
- background: rgba(0, 0, 0, 0.85);
135
- color: #00ff00;
136
- padding: 12px;
137
- font-family: 'Courier New', monospace;
138
- font-size: 11px;
139
- line-height: 1.4;
140
- border-radius: 6px;
141
- border: 1px solid rgba(0, 255, 0, 0.3);
142
- pointer-events: none;
143
- white-space: pre-line;
144
- min-width: 200px;
145
- z-index: 1003;
146
- }
147
-
148
- ${this.getNavigationStyles()}
149
- `
150
-
151
- const gaussianContent = `
152
- <canvas style="display: none;"></canvas>
153
- ${this.isLoading ? '<div class="loading">Loading...</div>' : ''}
154
- ${!this.isLoading && this.debugMode ? `
155
- <div class="debug-info">
156
- 📍 Camera Position:
157
- ${this.getCameraDebugInfo()}
158
-
159
- 🎯 Camera Target:
160
- ${this.getTargetDebugInfo()}
161
-
162
- 🎮 Controls:
163
- • Rotate: Left-drag
164
- • Zoom: Scroll wheel
165
- • Pan: Right-drag or Shift+Left-drag
166
-
167
- 📊 Status: ${this.scene ? 'Splat loaded' : 'Loading...'}
168
- </div>
169
- ` : ''}
170
- `
171
-
172
- const html = `
173
- ${styles}
174
- <div class="backdrop" style="${this.isShow ? 'visibility: visible' : 'visibility: hidden'}">
175
- <div class="viewer">
176
- <div class="gaussian-container">
177
- ${gaussianContent}
178
- </div>
179
- </div>
180
- ${this.getNavigationButtons()}
181
- </div>
182
- `
183
-
184
- this.updateShadowRoot(html)
185
-
186
- // Add navigation listeners after render
187
- setTimeout(() => {
188
- this.addNavigationListeners()
189
- }, 0)
190
- }
191
-
192
- private cleanup() {
193
- if (this.animationId) {
194
- cancelAnimationFrame(this.animationId)
195
- this.animationId = undefined
196
- }
197
-
198
- if (this.renderer && typeof this.renderer.dispose === 'function') {
199
- this.renderer.dispose()
200
- }
201
-
202
- // Remove all gaussian canvases from document.body
203
- const existingCanvases = document.querySelectorAll('canvas[id^="gaussian-canvas-"]')
204
- existingCanvases.forEach(canvas => {
205
- if (canvas.parentNode === document.body) {
206
- document.body.removeChild(canvas)
207
- }
208
- })
209
-
210
- this.scene = undefined
211
- this.camera = undefined
212
- this.renderer = undefined
213
- this.controls = undefined
214
- this.canvas = undefined
215
- }
216
-
217
- private getCameraDebugInfo(): string {
218
- if (!this.camera || !this.camera.position) return 'Position: unavailable'
219
- const pos = this.camera.position
220
- try {
221
- return `X: ${pos.x.toFixed(3)}, Y: ${pos.y.toFixed(3)}, Z: ${pos.z.toFixed(3)}`
222
- } catch (_error) {
223
- return `Position: ${JSON.stringify(pos)}`
224
- }
225
- }
226
-
227
- private getTargetDebugInfo(): string {
228
- if (!this.controls) return 'Target: controls unavailable'
229
- // gsplat.js OrbitControls might not have a target property
230
- // Return available control info instead
231
- try {
232
- return `Controls active (no target property in gsplat.js)`
233
- } catch (_error) {
234
- return `Target: ${JSON.stringify(this.controls)}`
235
- }
236
- }
237
-
238
- private updateDebugInfo() {
239
- // Update debug display
240
- const debugEl = this.query('.debug-info')
241
- if (debugEl) {
242
- debugEl.innerHTML = `
243
- 📍 Camera Position:
244
- ${this.getCameraDebugInfo()}
245
-
246
- 🎯 Camera Target:
247
- ${this.getTargetDebugInfo()}
248
-
249
- 🎮 Controls:
250
- • Rotate: Left-drag
251
- • Zoom: Scroll wheel
252
- • Pan: Right-drag or Shift+Left-drag
253
-
254
- 📊 Status: ${this.scene ? 'Splat loaded' : 'Loading...'}
255
- `
256
- }
257
- }
258
-
259
- private async initializeViewer() {
260
- // Create a unique ID for this instance
261
- const canvasId = `gaussian-canvas-${Date.now()}`
262
-
263
- // Get the viewer container dimensions
264
- const viewerEl = this.query('.viewer') as HTMLElement
265
- if (!viewerEl) return
266
-
267
- const rect = viewerEl.getBoundingClientRect()
268
-
269
- // Check if canvas already exists in normal DOM
270
- let normalCanvas = document.getElementById(canvasId) as HTMLCanvasElement
271
- if (!normalCanvas) {
272
- normalCanvas = document.createElement('canvas')
273
- normalCanvas.id = canvasId
274
- normalCanvas.style.position = 'fixed'
275
- normalCanvas.style.top = `${rect.top}px`
276
- normalCanvas.style.left = `${rect.left}px`
277
- normalCanvas.style.width = `${rect.width}px`
278
- normalCanvas.style.height = `${rect.height}px`
279
- normalCanvas.style.zIndex = '1001' // Above backdrop but below buttons
280
- normalCanvas.style.pointerEvents = 'auto' // Keep mouse events for 3D controls
281
- normalCanvas.style.display = 'block'
282
- normalCanvas.style.background = 'transparent'
283
- document.body.appendChild(normalCanvas)
284
- }
285
-
286
- // Update canvas position in case viewer moved
287
- normalCanvas.style.top = `${rect.top}px`
288
- normalCanvas.style.left = `${rect.left}px`
289
- normalCanvas.style.width = `${rect.width}px`
290
- normalCanvas.style.height = `${rect.height}px`
291
-
292
- this.canvas = normalCanvas
293
-
294
- try {
295
- const container = this.query('.gaussian-container') as HTMLDivElement
296
- if (!container) return
297
-
298
- // Import gsplat using the proven API approach
299
- const SPLAT = await import('gsplat')
300
-
301
- // Setup GSplat components exactly like the React example
302
- this.scene = new SPLAT.Scene()
303
- this.camera = new SPLAT.Camera()
304
- this.renderer = new SPLAT.WebGLRenderer(this.canvas)
305
- this.controls = new SPLAT.OrbitControls(this.camera, this.canvas)
306
-
307
- // Load the splat file
308
- await SPLAT.Loader.LoadAsync(this.splatUrl, this.scene)
309
-
310
- // Start render loop
311
- let frameCount = 0
312
- const animate = () => {
313
- // Check if renderer still exists before continuing
314
- if (!this.renderer || !this.scene || !this.camera) {
315
- return
316
- }
317
-
318
- this.animationId = requestAnimationFrame(animate)
319
-
320
- if (this.controls) {
321
- this.controls.update()
322
- }
323
-
324
- try {
325
- this.renderer.render(this.scene, this.camera)
326
- } catch (_e) {
327
- }
328
-
329
- // Log first frame only
330
- if (frameCount === 0 && this.canvas) {
331
- frameCount++
332
- }
333
-
334
- if (this.debugMode) {
335
- this.updateDebugInfo()
336
- }
337
- }
338
- animate()
339
-
340
- // Setup resize handler
341
- const resizeObserver = new ResizeObserver(() => {
342
- this.handleResize()
343
- })
344
- resizeObserver.observe(container)
345
-
346
- } catch (error) {
347
- throw error
348
- }
349
- }
350
-
351
- private handleResize() {
352
- const container = this.query('.gaussian-container') as HTMLDivElement
353
- if (!container || !this.renderer || !this.camera) return
354
-
355
- const width = container.clientWidth
356
- const height = container.clientHeight
357
-
358
- // Update renderer size
359
- if (typeof this.renderer.setSize === 'function') {
360
- this.renderer.setSize(width, height)
361
- }
362
-
363
- // Update camera aspect ratio
364
- if (typeof this.camera.aspect !== 'undefined') {
365
- this.camera.aspect = width / height
366
- if (typeof this.camera.updateProjectionMatrix === 'function') {
367
- this.camera.updateProjectionMatrix()
368
- }
369
- }
370
- }
371
- }
372
-
373
- if (!customElements.get('cc-viewer-gaussian')) {
374
- customElements.define('cc-viewer-gaussian', CcViewerGaussian)
375
- }
376
-
377
- declare global {
378
- interface HTMLElementTagNameMap {
379
- 'cc-viewer-gaussian': CcViewerGaussian
380
- }
1
+ import { CcViewerBase } from './cc-viewer-base'
2
+
3
+ export class CcViewerGaussian extends CcViewerBase {
4
+ private splatUrl = ''
5
+ private debugMode = false
6
+ private cameraPosition = '3,3,3'
7
+ private _cameraTarget = '0,0,0' // TODO: Implement camera target functionality
8
+
9
+ // gsplat.js library types are not available, using any for external library objects
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ private scene?: any
12
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
+ private camera?: any
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ private renderer?: any
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ private controls?: any
18
+ private animationId?: number
19
+ private canvas?: HTMLCanvasElement
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ private swiper?: any
22
+
23
+ static get observedAttributes() {
24
+ return ['show', 'debug-mode', 'camera-position', 'camera-target']
25
+ }
26
+
27
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {
28
+ if (name === 'show') {
29
+ this.isShow = newValue === 'true'
30
+ } else if (name === 'debug-mode') {
31
+ this.debugMode = newValue === 'true'
32
+ } else if (name === 'camera-position') {
33
+ this.cameraPosition = newValue || '3,3,3'
34
+ } else if (name === 'camera-target') {
35
+ this._cameraTarget = newValue || '0,0,0'
36
+ }
37
+ super.attributeChangedCallback(name, oldValue, newValue)
38
+ }
39
+
40
+ // Implementation of abstract methods from base class
41
+ protected async doOpen(url: string): Promise<void> {
42
+ this.splatUrl = url
43
+ await this.initializeViewer()
44
+ }
45
+
46
+ protected doClose(): void {
47
+ this.cleanup()
48
+ }
49
+
50
+ protected getViewerContent(): string {
51
+ // This won't be used since we override render, but required by abstract class
52
+ return ''
53
+ }
54
+
55
+ // Override to use custom rendering due to special canvas handling
56
+ protected shouldUseCustomRender(): boolean {
57
+ return true
58
+ }
59
+
60
+ // Custom render implementation
61
+ protected customRender(): void {
62
+ const styles = this.css`
63
+ :host {
64
+ --cc-viewer-z-index-each: 1000;
65
+ }
66
+
67
+ .backdrop {
68
+ justify-content: center;
69
+ align-items: center;
70
+ position: fixed;
71
+ left: 0;
72
+ right: 0;
73
+ top: 0;
74
+ bottom: 0;
75
+ width: 100%;
76
+ height: 100%;
77
+ background-color: rgba(0, 0, 0, 0.9);
78
+ z-index: 1000;
79
+ }
80
+
81
+ .viewer {
82
+ position: absolute;
83
+ width: 90%;
84
+ height: 85%;
85
+ inset: 0px;
86
+ margin: auto;
87
+ align-self: center;
88
+ background-color: #000;
89
+ }
90
+
91
+ .gaussian-container {
92
+ width: 100%;
93
+ height: 100%;
94
+ position: relative;
95
+ background: #000;
96
+ }
97
+
98
+ .gaussian-container canvas {
99
+ width: 100% !important;
100
+ height: 100% !important;
101
+ display: block;
102
+ }
103
+
104
+ .loading {
105
+ position: absolute;
106
+ top: 50%;
107
+ left: 50%;
108
+ transform: translate(-50%, -50%);
109
+ color: #fff;
110
+ font-size: 1.2rem;
111
+ }
112
+
113
+ .error {
114
+ position: absolute;
115
+ top: 50%;
116
+ left: 50%;
117
+ transform: translate(-50%, -50%);
118
+ color: #e74c3c;
119
+ text-align: center;
120
+ padding: 20px;
121
+ }
122
+
123
+ canvas {
124
+ display: block;
125
+ width: 100%;
126
+ height: 100%;
127
+ touch-action: none;
128
+ }
129
+
130
+ .debug-info {
131
+ position: absolute;
132
+ top: 10px;
133
+ left: 10px;
134
+ background: rgba(0, 0, 0, 0.85);
135
+ color: #00ff00;
136
+ padding: 12px;
137
+ font-family: 'Courier New', monospace;
138
+ font-size: 11px;
139
+ line-height: 1.4;
140
+ border-radius: 6px;
141
+ border: 1px solid rgba(0, 255, 0, 0.3);
142
+ pointer-events: none;
143
+ white-space: pre-line;
144
+ min-width: 200px;
145
+ z-index: 1003;
146
+ }
147
+
148
+ ${this.getNavigationStyles()}
149
+ `
150
+
151
+ const gaussianContent = `
152
+ <canvas style="display: none;"></canvas>
153
+ ${this.isLoading ? '<div class="loading">Loading...</div>' : ''}
154
+ ${!this.isLoading && this.debugMode ? `
155
+ <div class="debug-info">
156
+ 📍 Camera Position:
157
+ ${this.getCameraDebugInfo()}
158
+
159
+ 🎯 Camera Target:
160
+ ${this.getTargetDebugInfo()}
161
+
162
+ 🎮 Controls:
163
+ • Rotate: Left-drag
164
+ • Zoom: Scroll wheel
165
+ • Pan: Right-drag or Shift+Left-drag
166
+
167
+ 📊 Status: ${this.scene ? 'Splat loaded' : 'Loading...'}
168
+ </div>
169
+ ` : ''}
170
+ `
171
+
172
+ const html = `
173
+ ${styles}
174
+ <div class="backdrop" style="${this.isShow ? 'visibility: visible' : 'visibility: hidden'}">
175
+ <div class="viewer">
176
+ <div class="gaussian-container">
177
+ ${gaussianContent}
178
+ </div>
179
+ </div>
180
+ ${this.getNavigationButtons()}
181
+ </div>
182
+ `
183
+
184
+ this.updateShadowRoot(html)
185
+
186
+ // Add navigation listeners after render
187
+ setTimeout(() => {
188
+ this.addNavigationListeners()
189
+ }, 0)
190
+ }
191
+
192
+ private cleanup() {
193
+ if (this.animationId) {
194
+ cancelAnimationFrame(this.animationId)
195
+ this.animationId = undefined
196
+ }
197
+
198
+ if (this.renderer && typeof this.renderer.dispose === 'function') {
199
+ this.renderer.dispose()
200
+ }
201
+
202
+ // Remove all gaussian canvases from document.body
203
+ const existingCanvases = document.querySelectorAll('canvas[id^="gaussian-canvas-"]')
204
+ existingCanvases.forEach(canvas => {
205
+ if (canvas.parentNode === document.body) {
206
+ document.body.removeChild(canvas)
207
+ }
208
+ })
209
+
210
+ this.scene = undefined
211
+ this.camera = undefined
212
+ this.renderer = undefined
213
+ this.controls = undefined
214
+ this.canvas = undefined
215
+ }
216
+
217
+ private getCameraDebugInfo(): string {
218
+ if (!this.camera || !this.camera.position) return 'Position: unavailable'
219
+ const pos = this.camera.position
220
+ try {
221
+ return `X: ${pos.x.toFixed(3)}, Y: ${pos.y.toFixed(3)}, Z: ${pos.z.toFixed(3)}`
222
+ } catch (_error) {
223
+ return `Position: ${JSON.stringify(pos)}`
224
+ }
225
+ }
226
+
227
+ private getTargetDebugInfo(): string {
228
+ if (!this.controls) return 'Target: controls unavailable'
229
+ // gsplat.js OrbitControls might not have a target property
230
+ // Return available control info instead
231
+ try {
232
+ return `Controls active (no target property in gsplat.js)`
233
+ } catch (_error) {
234
+ return `Target: ${JSON.stringify(this.controls)}`
235
+ }
236
+ }
237
+
238
+ private updateDebugInfo() {
239
+ // Update debug display
240
+ const debugEl = this.query('.debug-info')
241
+ if (debugEl) {
242
+ debugEl.innerHTML = `
243
+ 📍 Camera Position:
244
+ ${this.getCameraDebugInfo()}
245
+
246
+ 🎯 Camera Target:
247
+ ${this.getTargetDebugInfo()}
248
+
249
+ 🎮 Controls:
250
+ • Rotate: Left-drag
251
+ • Zoom: Scroll wheel
252
+ • Pan: Right-drag or Shift+Left-drag
253
+
254
+ 📊 Status: ${this.scene ? 'Splat loaded' : 'Loading...'}
255
+ `
256
+ }
257
+ }
258
+
259
+ private async initializeViewer() {
260
+ // Create a unique ID for this instance
261
+ const canvasId = `gaussian-canvas-${Date.now()}`
262
+
263
+ // Get the viewer container dimensions
264
+ const viewerEl = this.query('.viewer') as HTMLElement
265
+ if (!viewerEl) return
266
+
267
+ const rect = viewerEl.getBoundingClientRect()
268
+
269
+ // Check if canvas already exists in normal DOM
270
+ let normalCanvas = document.getElementById(canvasId) as HTMLCanvasElement
271
+ if (!normalCanvas) {
272
+ normalCanvas = document.createElement('canvas')
273
+ normalCanvas.id = canvasId
274
+ normalCanvas.style.position = 'fixed'
275
+ normalCanvas.style.top = `${rect.top}px`
276
+ normalCanvas.style.left = `${rect.left}px`
277
+ normalCanvas.style.width = `${rect.width}px`
278
+ normalCanvas.style.height = `${rect.height}px`
279
+ normalCanvas.style.zIndex = '1001' // Above backdrop but below buttons
280
+ normalCanvas.style.pointerEvents = 'auto' // Keep mouse events for 3D controls
281
+ normalCanvas.style.display = 'block'
282
+ normalCanvas.style.background = 'transparent'
283
+ document.body.appendChild(normalCanvas)
284
+ }
285
+
286
+ // Update canvas position in case viewer moved
287
+ normalCanvas.style.top = `${rect.top}px`
288
+ normalCanvas.style.left = `${rect.left}px`
289
+ normalCanvas.style.width = `${rect.width}px`
290
+ normalCanvas.style.height = `${rect.height}px`
291
+
292
+ this.canvas = normalCanvas
293
+
294
+ try {
295
+ const container = this.query('.gaussian-container') as HTMLDivElement
296
+ if (!container) return
297
+
298
+ // Import gsplat using the proven API approach
299
+ const SPLAT = await import('gsplat')
300
+
301
+ // Setup GSplat components exactly like the React example
302
+ this.scene = new SPLAT.Scene()
303
+ this.camera = new SPLAT.Camera()
304
+ this.renderer = new SPLAT.WebGLRenderer(this.canvas)
305
+ this.controls = new SPLAT.OrbitControls(this.camera, this.canvas)
306
+
307
+ // Load the splat file
308
+ await SPLAT.Loader.LoadAsync(this.splatUrl, this.scene)
309
+
310
+ // Start render loop
311
+ let frameCount = 0
312
+ const animate = () => {
313
+ // Check if renderer still exists before continuing
314
+ if (!this.renderer || !this.scene || !this.camera) {
315
+ return
316
+ }
317
+
318
+ this.animationId = requestAnimationFrame(animate)
319
+
320
+ if (this.controls) {
321
+ this.controls.update()
322
+ }
323
+
324
+ try {
325
+ this.renderer.render(this.scene, this.camera)
326
+ } catch (_e) {
327
+ }
328
+
329
+ // Log first frame only
330
+ if (frameCount === 0 && this.canvas) {
331
+ frameCount++
332
+ }
333
+
334
+ if (this.debugMode) {
335
+ this.updateDebugInfo()
336
+ }
337
+ }
338
+ animate()
339
+
340
+ // Setup resize handler
341
+ const resizeObserver = new ResizeObserver(() => {
342
+ this.handleResize()
343
+ })
344
+ resizeObserver.observe(container)
345
+
346
+ } catch (error) {
347
+ throw error
348
+ }
349
+ }
350
+
351
+ private handleResize() {
352
+ const container = this.query('.gaussian-container') as HTMLDivElement
353
+ if (!container || !this.renderer || !this.camera) return
354
+
355
+ const width = container.clientWidth
356
+ const height = container.clientHeight
357
+
358
+ // Update renderer size
359
+ if (typeof this.renderer.setSize === 'function') {
360
+ this.renderer.setSize(width, height)
361
+ }
362
+
363
+ // Update camera aspect ratio
364
+ if (typeof this.camera.aspect !== 'undefined') {
365
+ this.camera.aspect = width / height
366
+ if (typeof this.camera.updateProjectionMatrix === 'function') {
367
+ this.camera.updateProjectionMatrix()
368
+ }
369
+ }
370
+ }
371
+ }
372
+
373
+ if (!customElements.get('cc-viewer-gaussian')) {
374
+ customElements.define('cc-viewer-gaussian', CcViewerGaussian)
375
+ }
376
+
377
+ declare global {
378
+ interface HTMLElementTagNameMap {
379
+ 'cc-viewer-gaussian': CcViewerGaussian
380
+ }
381
381
  }