architwin 1.0.14 → 1.0.16

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.
Files changed (3) hide show
  1. package/architwin.ts +1656 -0
  2. package/package.json +5 -4
  3. package/types.ts +185 -0
package/architwin.ts ADDED
@@ -0,0 +1,1656 @@
1
+ import axios from "axios";
2
+ import type { MpSdk, ShowcaseBundleWindow, Scene } from "./bundle/sdk";
3
+ import type {
4
+ ITag,
5
+ IUser,
6
+ ISpace,
7
+ IShowcaseObject,
8
+ I3DObject,
9
+ IMPConfig,
10
+ ISweep,
11
+ IVideoObject,
12
+ ITagPublic,
13
+ NearbyPayload,
14
+ NearbyObjects,
15
+ ISlideshowObject,
16
+ IObjectData,
17
+
18
+ } from "./types";
19
+ import { distance} from "mathjs";
20
+ // @ts-ignore
21
+ // import * as _config from './config.json'
22
+ import JSZip from "jszip";
23
+ import JSZipUtils from "jszip-utils";
24
+
25
+ const _config = {
26
+ aws: {
27
+ region: "ap-northeast-1",
28
+ accessKeyId: "AKIAVVUXZ66KW7GBSW7A",
29
+ secretAccessKey: "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'",
30
+ },
31
+ mp: {
32
+ sdkKey: "a3ae8341bd8f44899eba16df86307d7d",
33
+ urlParams: [
34
+ "help",
35
+ "play",
36
+ "nt",
37
+ "play",
38
+ "qs",
39
+ "brand",
40
+ "dh",
41
+ "tour",
42
+ "gt",
43
+ "hr",
44
+ "mls",
45
+ "mt",
46
+ "tagNav",
47
+ "pin",
48
+ "portal",
49
+ "f",
50
+ "fp",
51
+ "lang",
52
+ "kb",
53
+ "lp",
54
+ "st",
55
+ "title",
56
+ "tourcta",
57
+ "wts",
58
+ "ts",
59
+ "hl",
60
+ "vr",
61
+ "nozoom",
62
+ "search",
63
+ "wh",
64
+ ],
65
+ },
66
+ };
67
+
68
+ const apiURL = "http://localhost:5173/api/v1/";
69
+ const sdkKey = _config.mp.sdkKey;
70
+ const urlParams = _config.mp.urlParams;
71
+
72
+ console.log("apiURL...", apiURL);
73
+ // start here
74
+ let _api = {} as any;
75
+ let _space = {} as ISpace;
76
+ let _atwin = {} as MpSdk;
77
+ let _tags = [] as ITag[];
78
+
79
+ let tags = [] as ITagPublic[];
80
+ let sweeps = [] as any;
81
+
82
+ let _allSlideShow = [] as ISlideshowObject[]
83
+ let _slideShowImage = [] as any
84
+ let _currentSlideShowID = 0
85
+ let _currentSlideIndex = 0
86
+ let _currentSlideShow = null
87
+ var _timer = null
88
+ let _videos = [] as IVideoObject[]
89
+ let _currentViewMode = "" as MpSdk.Mode.Mode | null
90
+ let _3DXObject = [] as Scene.INode[]
91
+ let _unrenderedObjects = [] as IShowcaseObject[]
92
+ let _transformControlNode:Scene.INode
93
+ let _inputControlComponent = null
94
+ let _selectedObject = {} as IObjectData
95
+ let previousTimestamp:number = 0;
96
+
97
+ let _currentSweep = {} as ISweep;
98
+ let _currentCameraPose = {} as any;
99
+ let _sweeps = [] as any;
100
+
101
+
102
+
103
+ // end here
104
+
105
+
106
+ let _atwinSrc = `/bundle/showcase.html?m=''&applicationKey=${sdkKey}&newtags=1`;
107
+
108
+ // =========================================== INITIALIZATION ===========================================
109
+
110
+ // initialization: loginUser, iframe, space, tags, objects
111
+ async function connectSpace(
112
+ url: string,
113
+ auth: { apiKey: string; user: IUser },
114
+ config: IMPConfig
115
+ ) {
116
+ console.log("connectSpace()");
117
+
118
+ console.log("__config", config);
119
+
120
+ const api = axios.create({
121
+ baseURL: apiURL,
122
+ headers: {
123
+ "Access-Control-Allow-Origin": "*",
124
+ "Content-Type": "application/json",
125
+ Authorization: auth.apiKey,
126
+ },
127
+ });
128
+
129
+ _api = api;
130
+
131
+ const showcase = document.getElementById(
132
+ config.iframeId
133
+ ) as HTMLIFrameElement;
134
+
135
+ const spaceId = getSpaceId(url);
136
+
137
+ if (!spaceId) {
138
+ console.error("spaceId is undefined");
139
+ return;
140
+ }
141
+
142
+ await loginUser(auth.user);
143
+
144
+ const space = await getSpace(spaceId);
145
+
146
+ if (!space) {
147
+ console.error("space is undefined");
148
+ return;
149
+ }
150
+
151
+ setSpace(space);
152
+
153
+ _atwinSrc = getIframeSrc(config);
154
+
155
+ console.log("__atwinSrc", _atwinSrc);
156
+
157
+ showcase.src = _atwinSrc;
158
+
159
+ const tags = await getTags(space);
160
+
161
+ if (!tags) {
162
+ console.log("tags is undefined");
163
+ return;
164
+ }
165
+
166
+ setTags(tags);
167
+
168
+ const objects = await get3DObjects(spaceId);
169
+
170
+ if (!objects) {
171
+ console.log("objects is undefined");
172
+ return;
173
+ }
174
+
175
+ //setObjects(objects);
176
+
177
+ showcase.contentWindow?.location.reload(); // force reload iframe
178
+
179
+ const showcaseWindow = showcase.contentWindow as ShowcaseBundleWindow;
180
+
181
+ showcase.addEventListener("load", async () => {
182
+ console.log('iframe.addEventListener("load")');
183
+
184
+ console.log("showcaseWindow", showcaseWindow);
185
+
186
+ try {
187
+ _atwin = await showcaseWindow.MP_SDK.connect(showcaseWindow);
188
+ console.log("Hello Bundle SDK", _atwin);
189
+ onShowcaseConnect();
190
+ } catch (e) {
191
+ console.error(e);
192
+ }
193
+ });
194
+ }
195
+
196
+ // start here
197
+ // After connectSpace it will render: tags, objects, and listeners
198
+ async function onShowcaseConnect(): Promise<void> {
199
+ console.log("onShowcaseConnect()");
200
+ try {
201
+ const modelData = await _atwin.Model.getData();
202
+ console.log("Model sid:" + modelData.sid);
203
+
204
+ _atwin.App.state.subscribe(async (appState) => {
205
+ console.log("appState", appState);
206
+
207
+ if (appState.phase === _atwin.App.Phase.LOADING) {
208
+ console.log("App is loading...");
209
+ await hideTags();
210
+ } else if (appState.phase === _atwin.App.Phase.STARTING) {
211
+ console.log("App is starting...");
212
+ await showTags(_tags);
213
+ } else if (appState.phase === _atwin.App.Phase.PLAYING) {
214
+ console.log("App is playing...");
215
+ await getSweeps();
216
+ await setLighting()
217
+ _atwin.Sweep.current.subscribe(function (currentSweep) {
218
+ // Change to the current sweep has occurred.
219
+ if (currentSweep.sid === "") {
220
+ console.log("Not currently stationed at a sweep position");
221
+ } else {
222
+ _currentSweep = currentSweep;
223
+ console.log("Currently at sweep", _currentSweep.id);
224
+
225
+ // check auto play
226
+ videoAutoPlay()
227
+
228
+ // console.log("_3DXObject "+_3DXObject.length)
229
+ // _3DXObject.forEach(obj => {
230
+ // console.log("3D Position "+ JSON.stringify(obj.position))
231
+ // })
232
+ }
233
+ });
234
+
235
+ _atwin.Camera.pose.subscribe(async function (pose) {
236
+ // Changes to the Camera pose have occurred.
237
+ _currentCameraPose = pose;
238
+ console.log("Current Camera Pose", _currentCameraPose);
239
+ const hasElapsed = hasTimeElapsed(300)
240
+ if (hasElapsed) {
241
+ console.log("_unrenderedObjects "+_unrenderedObjects.length)
242
+ await renderOnDemand()
243
+ }
244
+ if (hasTimeElapsed(1000)) {
245
+ getNearbyObjects({type: 'ALL',distance:2})
246
+ }
247
+ });
248
+
249
+ _atwin.Mode.current.subscribe((mode) => {
250
+ _currentViewMode = mode;
251
+ });
252
+ }
253
+ });
254
+ } catch (e) {
255
+ console.error(e);
256
+ }
257
+ }
258
+
259
+ // end here
260
+
261
+
262
+ /**
263
+ * Super light weight alternative to lodash's debounce
264
+ * This function will allow you to debounce a function a set amoint in milliseconds
265
+ * Useful for improving performance if your function is inside a subscribe method
266
+ * @param maxTime milliseconds
267
+ * @returns {boolean}
268
+ */
269
+
270
+ function hasTimeElapsed(maxTime:number):boolean {
271
+ const currentTimestamp = Date.now();
272
+
273
+ // Difference in milliseconds
274
+ const differenceInMilliseconds = (currentTimestamp - previousTimestamp) % 1000;
275
+ previousTimestamp = currentTimestamp
276
+ if (differenceInMilliseconds >= maxTime) {
277
+ console.log(`Elapsed more than ${maxTime}`);
278
+ return true
279
+ } else {
280
+ console.log(`Elapsed less than ${maxTime}`);
281
+ return false
282
+ }
283
+ }
284
+
285
+ /**
286
+ * This function detects and return objects fetched from the API that have NOT been rendered yet
287
+ * Not to be confused with getNearbyObjects which returns INodes that have already been rendered
288
+ * @constructor
289
+ * @param payload
290
+ * @type {NearbyPayload}
291
+ * @returns {Array<IShowcaseObject>}
292
+ */
293
+
294
+ function getNearbyUnrenderedObjects(payload:NearbyPayload):Array<IShowcaseObject>{
295
+ let toBeRendered = [] as IShowcaseObject[]
296
+ const currentPose = {
297
+ x: _currentCameraPose.position.x,
298
+ y: _currentCameraPose.position.y
299
+ }
300
+ toBeRendered = _unrenderedObjects.filter(obj => {
301
+ const obj_pos = {
302
+ x: obj.object_position.x,
303
+ y: obj.object_position.y,
304
+ }
305
+ const distance = calculateDistance(currentPose,obj_pos)
306
+ return distance < payload.distance
307
+ })
308
+
309
+ const filtered = _unrenderedObjects.filter(obj=> {
310
+ return toBeRendered.indexOf(obj) === -1
311
+ })
312
+ _unrenderedObjects = filtered
313
+ console.log("render toBeRendered "+ toBeRendered.length)
314
+ return toBeRendered
315
+ }
316
+
317
+ /**
318
+ * This function returns an object that returns an array of 3D, video, and slideshow objects
319
+ * You can pass a object type as a object parameter to set what the contents of the returned objects will be
320
+ * @constructor
321
+ * @param payload
322
+ * @type {NearbyPayload}
323
+ * @returns {NearbyObjects}
324
+ */
325
+
326
+ function getNearbyObjects(payload:NearbyPayload):NearbyObjects{
327
+ if (payload.type === undefined || payload.type === '') {
328
+ payload.type = 'ALL'
329
+ }
330
+ if (payload.distance === undefined) {
331
+ payload.distance = 2
332
+ }
333
+
334
+ const pos1 = {
335
+ x: _currentCameraPose.position.x,
336
+ y: _currentCameraPose.position.y
337
+ }
338
+ let three_d = [] as Scene.INode[]
339
+ let videos = [] as IVideoObject[]
340
+ let slideshows = [] as ISlideshowObject[]
341
+ if (payload.type === 'ALL' || '3DX') {
342
+ three_d = _3DXObject.filter(obj => {
343
+ const pos2 = {
344
+ x: obj.position.x,
345
+ y: obj.position.y
346
+ }
347
+ const distance = calculateDistance(pos1,pos2)
348
+ console.log("3DX Distance: "+distance)
349
+ return distance < payload.distance
350
+ })
351
+ }
352
+ if (payload.type === 'ALL' || 'VIDEO') {
353
+ videos = _videos.filter(vid => {
354
+ const pos2 = {
355
+ x: vid.node.position.x,
356
+ y: vid.node.position.y
357
+ }
358
+ const distance = calculateDistance(pos1,pos2)
359
+ console.log("Video Distance: "+distance)
360
+ return distance < payload.distance
361
+ })
362
+ }
363
+
364
+ if(payload.type === 'ALL' || 'SLIDESHOW'){
365
+
366
+ slideshows = _allSlideShow.filter(slide => {
367
+ const pos2 = {
368
+ x: slide.node.position.x,
369
+ y: slide.node.position.y
370
+ }
371
+ const distance = calculateDistance(pos1,pos2)
372
+ console.log("Slideshow Distance: "+distance)
373
+ return distance < payload.distance
374
+ })
375
+ }
376
+
377
+ console.log("nearby3DXObjects "+three_d.length)
378
+ console.log("nearbyVideos "+videos.length)
379
+ console.log("nearbySlideshows "+slideshows.length)
380
+ return {
381
+ x3d: three_d,
382
+ videos: videos,
383
+ slideshows: slideshows
384
+ }
385
+ }
386
+
387
+ async function show3DObjects(object: IShowcaseObject): Promise < void> {
388
+ const [sceneObject] = await _atwin.Scene.createObjects(1);
389
+ const id = Math.floor(Math.random() * 20);
390
+ const modelNode = sceneObject.addNode(id.toString());
391
+ const component = modelNode.addComponent(getComponentLoader(object), {
392
+ url: object.object_data?.amazon_uri,
393
+ });
394
+ if (!component.inputs) {
395
+ console.error("component.inputs is undefined");
396
+ return;
397
+ }
398
+
399
+ component.inputs.localScale = {
400
+ x: object.object_scale.x,
401
+ y: object.object_scale.y,
402
+ z: object.object_scale.z,
403
+ };
404
+
405
+ // @ts-ignore
406
+ modelNode.obj3D.position.set(
407
+ object.object_position.x,
408
+ object.object_position.y,
409
+ object.object_position.z
410
+ );
411
+
412
+ // @ts-ignore
413
+ modelNode.obj3D.rotation.set(
414
+ object.object_rotation.x,
415
+ object.object_rotation.y,
416
+ object.object_rotation.z
417
+ );
418
+
419
+ _3DXObject.push(modelNode)
420
+ console.log("_3DXObject "+_3DXObject.length)
421
+ modelNode.start();
422
+
423
+ //Spy on component
424
+ component.spyOnEvent(new ClickSpy(object,modelNode,component))
425
+ }
426
+
427
+ /**
428
+ * Renders objects if within set range
429
+ * This function SHOULD be debounced for better performance
430
+ * @constructor
431
+ */
432
+ async function renderOnDemand () {
433
+ if (_unrenderedObjects.length !== 0) {
434
+ console.log("renderOnDemand()")
435
+ const objects = getNearbyUnrenderedObjects({type:'',distance:2})
436
+ objects.forEach(async (obj) => {
437
+ if (
438
+ obj.object_data?.object_type === "FBX" ||
439
+ obj.object_data?.object_type === "GLB"
440
+ ) {
441
+ await show3DObjects(obj);
442
+ }else if (obj.object_data?.object_type === "MP4") {
443
+ await showVideoObjects(obj);
444
+ } else if (obj.object_data?.object_type === "ZIP") {
445
+ await showSlideScreenModel(obj);
446
+ }
447
+ })
448
+ }
449
+ }
450
+
451
+
452
+ // get iframe src with configurations (mp url parameters)
453
+ function getIframeSrc(config: IMPConfig): string {
454
+ console.log("getIframeSrc()", config);
455
+
456
+ const modelId = _space.space_url.split("?m=")[1];
457
+
458
+ if (!modelId) {
459
+ console.error("modelId is undefined");
460
+ return "";
461
+ }
462
+
463
+ let src = ``;
464
+ src += `architwin/bundle/showcase.html?m=${modelId}&applicationKey=${sdkKey}&newtags=1`;
465
+
466
+ for (let param of urlParams) {
467
+ if (param in config) {
468
+ // @ts-ignore
469
+ src += `&${param}=${config[param]}`;
470
+ }
471
+ }
472
+
473
+ return src;
474
+ }
475
+
476
+ // login user to fix cors error
477
+ async function loginUser(user: IUser) {
478
+ console.log("loginUser(user)", user);
479
+
480
+ const response = await _api.post(
481
+ "/cas/tickets?email=" + user.email + "&password=" + user.password
482
+ );
483
+ console.log("loginUser, response", response.status, response.data);
484
+ if (response.status == 200) {
485
+ return response.data;
486
+ } else {
487
+ return response;
488
+ }
489
+ }
490
+
491
+ // get showcase/space data in api
492
+ async function getSpace(spaceId: string): Promise<ISpace | null> {
493
+ console.log("getShowcase(spaceId: string)", spaceId);
494
+
495
+ const response = await _api.get(`showcases/id/${spaceId}`);
496
+ console.log("response", response);
497
+
498
+ if (response.status === 200) {
499
+ if (response.data.data.length === 0) {
500
+ console.error("No data");
501
+ }
502
+
503
+ return response.data.data[0];
504
+ } else {
505
+ console.error("Error in fetchShowcase()" + response);
506
+ }
507
+ return null;
508
+ }
509
+
510
+ function getSpaceId(url: string) {
511
+ const urlArray = url.split(
512
+ /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/
513
+ );
514
+ const path = urlArray[5];
515
+ const spaceId = path.split("/")[3];
516
+
517
+ console.log("urlArray", urlArray);
518
+ console.log("path", path);
519
+
520
+ if (!spaceId) {
521
+ console.error("spaceId is undefined");
522
+ return;
523
+ }
524
+ return spaceId;
525
+ }
526
+
527
+ function setSpace(space: ISpace): void {
528
+ console.log("setSpace(space: ISpace)", space);
529
+ _space = space;
530
+ }
531
+
532
+ // get tags from api
533
+ async function getTags(space: ISpace): Promise<ITag[] | null> {
534
+ console.log("getTags()");
535
+
536
+ try {
537
+ const response = await _api.get(`tags/showcase-id/${space.id}`);
538
+ console.log("response", response);
539
+ if (response.status === 200) {
540
+ const tags = response.data.data;
541
+
542
+ if (!tags) {
543
+ console.error("tags is undefined");
544
+ }
545
+
546
+ return tags;
547
+ } else {
548
+ console.error("Custom Error: Unable to fetch tags");
549
+ }
550
+ } catch (error) {
551
+ console.error(error);
552
+ }
553
+
554
+ return null;
555
+ }
556
+
557
+ async function setTags(tags: ITag[]) {
558
+ console.log("setTags()", tags);
559
+ _tags = tags.map((tag) => {
560
+ //@ts-ignore
561
+ tag.json_data = JSON.parse(tag.json_data);
562
+ //@ts-ignore
563
+ tag.json_data.id = tag.json_data.sid;
564
+ return tag;
565
+ });
566
+
567
+ mapTags(_tags);
568
+ }
569
+
570
+ // get objects from api
571
+ async function get3DObjects(showcase_id: string): Promise<IShowcaseObject[]> {
572
+ //This function will return the 3d objects associated with each
573
+ if (showcase_id) {
574
+ try {
575
+ const id = parseInt(showcase_id);
576
+
577
+ console.log("showcase_id " + id);
578
+
579
+ const response = await _api.get(`showcase-objects/showcase-id/${id}`);
580
+
581
+ const showcase_objects: Array<IShowcaseObject> = response.data.data;
582
+
583
+ console.log("showcase_objects " + JSON.stringify(showcase_objects));
584
+
585
+ const getRequests = showcase_objects.map((obj) =>
586
+ _api.get(`objects/id/${obj.object_id}`)
587
+ );
588
+ try {
589
+ const response = await axios.all(getRequests);
590
+
591
+ const object_data: Array<I3DObject> = response.map((res) => {
592
+ return res.data.data[0];
593
+ });
594
+ console.log("object_data" + JSON.stringify(object_data));
595
+ const threed_objects: Array<IShowcaseObject> = showcase_objects.map(
596
+ (showcase) => {
597
+ const target = object_data.find(
598
+ (obj) => obj.id === showcase.object_id
599
+ );
600
+ if (target) {
601
+ showcase.object_data = target;
602
+ }
603
+ //parse string back to an object
604
+ if (showcase.object_position) {
605
+ showcase.object_position =
606
+ typeof showcase.object_position === "string"
607
+ ? JSON.parse(showcase.object_position)
608
+ : showcase.object_position;
609
+ }
610
+ if (showcase.object_rotation) {
611
+ showcase.object_rotation =
612
+ typeof showcase.object_rotation === "string"
613
+ ? JSON.parse(showcase.object_rotation)
614
+ : showcase.object_rotation;
615
+ }
616
+ if (showcase.object_scale) {
617
+ showcase.object_scale =
618
+ typeof showcase.object_scale === "string"
619
+ ? JSON.parse(showcase.object_scale)
620
+ : showcase.object_scale;
621
+ }
622
+ return showcase;
623
+ }
624
+ );
625
+
626
+ console.log(
627
+ "get3DObjectsByShowcaseId " + JSON.stringify(threed_objects)
628
+ );
629
+ _unrenderedObjects = threed_objects
630
+ return threed_objects;
631
+ } catch (error) {
632
+ console.error("threed_objects " + error);
633
+ }
634
+ } catch (error) {
635
+ console.error("get3DObjectsByShowcaseId " + error);
636
+ }
637
+ }
638
+ return [];
639
+ }
640
+
641
+ // function setObjects(objects: IShowcaseObject[]) {
642
+ // console.log("setObjects()", objects);
643
+ // _objects = objects;
644
+ // }
645
+
646
+ // =========================================== END INITIALIZATION ===========================================
647
+
648
+ // =========================================== TAGS FUNCTIONALITY ===========================================
649
+
650
+ // navigate to tag
651
+ async function gotoTag(tag_id: number): Promise<void> {
652
+ console.log("gotoTag(tag: tag_id)", tag_id);
653
+ if (!tag_id) {
654
+ console.error("tag is undefined");
655
+ return;
656
+ }
657
+ try {
658
+ const tag = _tags.find((i) => i.id === tag_id);
659
+
660
+ if (!tag) {
661
+ console.error("tag is not found");
662
+ return;
663
+ }
664
+ await _atwin.Mattertag.navigateToTag(
665
+ tag.json_data.id,
666
+ _atwin.Mattertag.Transition.FLY
667
+ );
668
+ } catch (error) {
669
+ console.error(error);
670
+ }
671
+ }
672
+
673
+ // render tags to space
674
+ async function showTags(tags: ITag[]): Promise<void> {
675
+ tags.forEach(async (tag, indx) => {
676
+ const mpData = tag.json_data;
677
+ if (!mpData) {
678
+ console.error("tag.json_data/mpData is undefined");
679
+ return;
680
+ }
681
+
682
+ const tagIds = await _atwin.Tag.add({
683
+ anchorPosition: mpData.anchorPosition,
684
+ color: mpData.color,
685
+ description: mpData.description,
686
+ //@ts-ignore
687
+ id: mpData.sid,
688
+ label: mpData.label,
689
+ stemVector: mpData.stemVector,
690
+ stemVisible: mpData.stemVisible,
691
+ });
692
+ // console.log('tagIds', tagIds)
693
+ if (tagIds) {
694
+ //@ts-ignore
695
+ tag.json_data.id = tagIds[0];
696
+
697
+ // register attachment
698
+ //@ts-ignore
699
+ if (
700
+ // @ts-ignore
701
+ tag.json_data.media &&
702
+ // @ts-ignore
703
+ // @ts-ignore
704
+ tag.json_data.media.src.trim() !== ""
705
+ ) {
706
+ try {
707
+ console.log("Attaching media...");
708
+ //@ts-ignore
709
+ const [attachmentId1] = await _atwin.Tag.registerAttachment(
710
+ // @ts-ignore
711
+ tag.json_data.media.src
712
+ );
713
+ tag.json_data["attachments"] = [attachmentId1];
714
+ //@ts-ignore
715
+ await _atwin.Tag.attach(tag.json_data.sid, attachmentId1);
716
+ console.log("Media successfully attached");
717
+ } catch (error) {
718
+ console.warn(
719
+ "Custom warn: Media not attached: Invalid media src link: " + error
720
+ );
721
+ //@ts-ignore
722
+ console.warn(
723
+ // @ts-ignore
724
+ `mediaSrc: ${tag.json_data.media.src} | tag index: ${indx}`
725
+ );
726
+ }
727
+ }
728
+
729
+ // if(_space.tagpin_image){
730
+ // const iconLink = _baseUurl + _uploadDir + _space.tagpin_image
731
+ // const textureId = _space.tagpin_image
732
+ // await _atwin.Asset.registerTexture(textureId, iconLink)
733
+ // await _atwin.Tag.editIcon(tag.json_data.id, textureId)
734
+ // }
735
+ }
736
+ });
737
+
738
+ console.log("tags", _tags);
739
+ }
740
+
741
+ // remove built in tags
742
+ async function hideTags(): Promise<void> {
743
+ console.log("hideTags()");
744
+ const tags = await _atwin.Mattertag.getData();
745
+ if (tags) {
746
+ const tagIds = tags.map((i: MpSdk.Mattertag.MattertagData) => i.sid);
747
+ await _atwin.Mattertag.remove(tagIds);
748
+ console.log("Tags removed in space: ", tagIds);
749
+ }
750
+ }
751
+
752
+ // only expose tag id and name for the tags array
753
+ function mapTags($tags: ITag[]) {
754
+ console.log("mapTags()", $tags);
755
+ tags = $tags.map((i) => {
756
+ const x = {} as ITagPublic;
757
+ x.id = i.id;
758
+ x.name = i.json_data.label;
759
+ return x;
760
+ });
761
+
762
+ console.log("tags", tags);
763
+ }
764
+
765
+ // =========================================== END TAGS FUNCTIONALITY ===========================================
766
+
767
+ // =========================================== SWEEP FUNCTIONALITY ===========================================
768
+
769
+ // get explicit location for currentSweep
770
+ function getCurrentSweep() {
771
+ return _currentSweep;
772
+ }
773
+
774
+ // get explicit location for currentPose
775
+ function getCurrentCameraPose() {
776
+ return _currentCameraPose;
777
+ }
778
+
779
+ // get explicit location for currentPose's position
780
+ function getCameraPosition() {
781
+ return _currentCameraPose.position;
782
+ }
783
+
784
+ // get explicit location for currentSweep's position
785
+ function getCurrentSweepPosition() {
786
+ return _currentSweep.position;
787
+ }
788
+
789
+ // gets all Sweeps in loaded Space
790
+ async function getSweeps(): Promise<ISweep | null> {
791
+ console.log("Getting Sweeps in Space");
792
+
793
+ // original implementation
794
+ _atwin.Sweep.data.subscribe({
795
+ onCollectionUpdated: function (collection) {
796
+ console.log("Sweeps In Space", collection);
797
+ if (!collection) {
798
+ console.log("No Sweeps in loaded Space");
799
+ }
800
+ _sweeps = collection;
801
+
802
+ const sw = Object.values(_sweeps);
803
+
804
+ sweeps = sw.map((item: any) => {
805
+ return {
806
+ id: item.uuid,
807
+ position: item.position,
808
+ neighbors: item.neighbors,
809
+ };
810
+ });
811
+ },
812
+ });
813
+
814
+ return null as any;
815
+ }
816
+
817
+
818
+ // moves to selected Sweep
819
+ async function moveToSweep(sweepId: string) {
820
+ // this is sweepData to be used in the moveTo
821
+ // const position = sweep.position;
822
+ const transition = _atwin.Sweep.Transition.FLY;
823
+ const transitionTime = 2000; // in milliseconds
824
+ console.log("Sweep Move", sweepId);
825
+
826
+ _atwin.Sweep.moveTo(sweepId, {
827
+ // position: position,
828
+ transition: transition,
829
+ transitionTime: transitionTime,
830
+ })
831
+ .then(function () {
832
+ // Move successful.
833
+ console.log("Sweep Arrived at sweep " + sweepId);
834
+ // getNearbySweeps(sweepId);
835
+ })
836
+ .catch(function (error) {
837
+ // Error with moveTo command
838
+ console.log("Sweep Error on Arriving", error);
839
+ });
840
+ }
841
+
842
+ // returns nearby Sweeps
843
+ async function getNearbySweeps(sweepId: string) {
844
+ if (sweepId) {
845
+ const nearby = await sweeps.find((item: any) => item.id == sweepId)
846
+ .neighbors;
847
+
848
+ console.log("Nearby Sweeps", nearby);
849
+ return nearby;
850
+ } else {
851
+ console.log("No Nearby Sweeps");
852
+ }
853
+ }
854
+
855
+
856
+ // =========================================== END SWEEP FUNCTIONALITY ===========================================
857
+
858
+ // =========================================== OBJECTS FUNCTIONALITY ===========================================
859
+
860
+ function getComponentLoader(object: IShowcaseObject) {
861
+ if (object && object.object_data) {
862
+ let index = object.object_data.object_type as string;
863
+
864
+ let component = {
865
+ FBX: _atwin.Scene.Component.FBX_LOADER,
866
+ GLB: _atwin.Scene.Component.GLTF_LOADER,
867
+ MP4: "liveVideo",
868
+ ZIP: "slideScreenModel",
869
+ };
870
+
871
+ return component[index] || "";
872
+ }
873
+
874
+ return "";
875
+ }
876
+
877
+ function setSelectedObject(data:IShowcaseObject,node:Scene.INode,component:Scene.IComponent) {
878
+ _selectedObject = {
879
+ object:data,
880
+ component: component,
881
+ node:node
882
+ }
883
+ console.log("setSelectedObject()")
884
+ }
885
+
886
+ async function setLighting() {
887
+ const [sceneObject] = await _atwin.Scene.createObjects(1);
888
+ const lights = sceneObject.addNode();
889
+
890
+ lights.addComponent("mp.directionalLight", {
891
+ intensity: 0.6,
892
+ color: { r: 1.0, g: 1.0, b: 1.0 },
893
+ });
894
+
895
+ lights.addComponent("mp.ambientLight", {
896
+ intensity: 0.6,
897
+ color: { r: 1.0, g: 1.0, b: 1.0 },
898
+ });
899
+
900
+ lights.start();
901
+ }
902
+
903
+ function clearTransformControls() {
904
+ if (!_transformControlNode ) {
905
+ console.error('_transformControlNode is undefined')
906
+ return
907
+ }
908
+ if (_inputControlComponent == null || !_inputControlComponent) {
909
+ console.error('_inputControlComponent is undefined')
910
+ return
911
+ }
912
+
913
+ _transformControlNode.stop()
914
+ //_inputControlComponent.stop()
915
+ console.log('clearTransformControls()')
916
+ }
917
+
918
+ async function setTransformControls(selectedObject:IObjectData,mode:string = 'translate') {
919
+ console.log("Object to be transformed "+selectedObject.object.object_data.name)
920
+
921
+ clearTransformControls()
922
+
923
+ const [ sceneObject ] = await _atwin.Scene.createObjects(1);
924
+ const transformNode = sceneObject.addNode();
925
+ const transformComponent = transformNode.addComponent('mp.transformControls')
926
+ _transformControlNode = transformNode
927
+
928
+ const inputComponent = transformNode.addComponent('mp.input', {
929
+ eventsEnabled: true,
930
+ userNavigationEnabled: true,
931
+ })
932
+
933
+ _inputControlComponent = inputComponent.spyOnEvent(new ClickSpy(selectedObject.object,selectedObject.node,selectedObject.component))
934
+ transformNode.start()
935
+ transformComponent.inputs.selection = selectedObject.node
936
+ transformComponent.inputs.mode = mode
937
+ }
938
+ // async function testObject() {
939
+ // const [ sceneObject ] = await _atwin.Scene.createObjects(1);
940
+ // const modelNode = sceneObject.addNode();
941
+ // const component = modelNode.addComponent(_atwin.Scene.Component.FBX_LOADER, {
942
+ // url: "https://cdn.jsdelivr.net/gh/mrdoob/three.js@dev/examples/models/fbx/stanford-bunny.fbx",
943
+ // });
944
+
945
+ // // Create a scene node with a transform control component.
946
+ // const node = sceneObject.addNode();
947
+ // const myControl = node.addComponent('mp.transformControls');
948
+
949
+ // node.start();
950
+ // component.inputs.localScale = {
951
+ // x: 0.00002,
952
+ // y: 0.00002,
953
+ // z: 0.00002
954
+ // };
955
+ // modelNode.obj3D.position.set(0,-1,0);
956
+ // modelNode.start()
957
+
958
+ // // Attach the model to the transform control
959
+ // myControl.inputs.selection = modelNode;
960
+ // // Make the transform control visible so that the user can manipulate the control selection.
961
+ // myControl.inputs.visible = true;
962
+
963
+ // // set 'translate' mode to position the selection.
964
+ // myControl.inputs.mode = 'translate';
965
+ // }
966
+ function calculateDistance(
967
+ pos1: { x: number; y: number },
968
+ pos2: { x: number; y: number }
969
+ ) {
970
+ const dx = pos2.x - pos1.x;
971
+ const dy = pos2.y - pos1.y;
972
+ return Math.sqrt(dx * dx + dy * dy);
973
+ }
974
+
975
+ async function showVideoObjects(object: IShowcaseObject) {
976
+ await createVideoComponent();
977
+
978
+ const [sceneObject] = await _atwin.Scene.createObjects(1);
979
+ const liveStreamNode = sceneObject.addNode();
980
+ var liveStreamComponent = await liveStreamNode.addComponent(
981
+ getComponentLoader(object),
982
+ {
983
+ url: object.object_data?.amazon_uri,
984
+ }
985
+ );
986
+
987
+ liveStreamNode.position.set(
988
+ object.object_position.x,
989
+ object.object_position.y,
990
+ object.object_position.z
991
+ );
992
+ // @ts-ignore
993
+ liveStreamNode.obj3D.rotation.set(
994
+ object.object_rotation.x,
995
+ object.object_rotation.y,
996
+ object.object_rotation.z
997
+ );
998
+
999
+ liveStreamNode.scale.set(
1000
+ object.object_scale.x,
1001
+ object.object_scale.y,
1002
+ object.object_scale.z
1003
+ );
1004
+
1005
+ if (liveStreamComponent.outputs.loadingState != "Error") {
1006
+ liveStreamNode.start();
1007
+ }
1008
+
1009
+ liveStreamComponent.inputs.src = object.object_data?.amazon_uri;
1010
+
1011
+ _videos.push({
1012
+ data: object,
1013
+ component: liveStreamComponent,
1014
+ node: liveStreamNode,
1015
+ type: 'VIDEO',
1016
+ } as IVideoObject);
1017
+ }
1018
+
1019
+ function playVideo(videoId: number) {
1020
+ let videoObject = _videos.find((object) => object.data.id == videoId);
1021
+
1022
+
1023
+ if (videoObject) {
1024
+ // @ts-ignore
1025
+ videoObject.component.video.play();
1026
+ } else {
1027
+ console.error("Unable to find video object using that id");
1028
+ }
1029
+ }
1030
+
1031
+ function pauseVideo(videoId: number) {
1032
+ let videoObject = _videos.find((object) => object.data.id == videoId);
1033
+
1034
+ if (videoObject) {
1035
+ // @ts-ignore
1036
+ videoObject.component.video.pause();
1037
+ } else {
1038
+ console.error("Unable to find video object using that id");
1039
+ }
1040
+ }
1041
+
1042
+ function videoAutoPlay() {
1043
+ async function nearestVideo(videoObject: IVideoObject) {
1044
+ let videoPosition = videoObject.data.object_position;
1045
+ let cameraPosition = getCurrentSweepPosition();
1046
+
1047
+ const computedDistance = distance(
1048
+ [cameraPosition.x, cameraPosition.y, cameraPosition.z],
1049
+ [videoPosition.x, videoPosition.y, videoPosition.z]
1050
+ );
1051
+
1052
+ try {
1053
+ if (
1054
+ videoObject.data.autoplay &&
1055
+ videoObject.data.autoplay_distance &&
1056
+ computedDistance < videoObject.data.autoplay_distance
1057
+ ) {
1058
+ // @ts-ignore
1059
+ videoObject.component.video.play();
1060
+ } else {
1061
+ // @ts-ignore
1062
+ videoObject.component.video.pause();
1063
+ }
1064
+ } catch (e) {
1065
+ console.error("Unable to play or stop video");
1066
+ }
1067
+ }
1068
+
1069
+ _videos.forEach((videoObject: IVideoObject) => nearestVideo(videoObject));
1070
+ }
1071
+
1072
+ function createVideoComponent() {
1073
+ function videoRenderer() {
1074
+ this.inputs = {
1075
+ visible: true,
1076
+ userNavigationEnabled: true,
1077
+ eventsEnabled: true,
1078
+ colliderEnabled: true,
1079
+ src: null,
1080
+ };
1081
+
1082
+ this.outputs = {
1083
+ texture: null,
1084
+ video: null,
1085
+ aspect: 720 / 480,
1086
+ };
1087
+
1088
+ this.onInit = function () {
1089
+ this.video;
1090
+ this.texture;
1091
+ };
1092
+
1093
+ this.onEvent = function (eventType, eventData) {};
1094
+
1095
+ this.onInputsUpdated = function (previous) {
1096
+ this.releaseTexture();
1097
+
1098
+ const THREE = this.context.three;
1099
+ if (!this.inputs.src) {
1100
+ this.video.src = "";
1101
+ return;
1102
+ }
1103
+
1104
+ if (this.inputs.src instanceof HTMLVideoElement) {
1105
+ this.video = this.inputs.src;
1106
+ } else {
1107
+ this.video = this.createVideoElement();
1108
+
1109
+ if (typeof this.inputs.src === "string") {
1110
+ this.video.src = this.inputs.src;
1111
+ } else {
1112
+ this.video.srcObject = this.inputs.src;
1113
+ }
1114
+
1115
+ this.video.load();
1116
+ }
1117
+
1118
+ this.texture = new THREE.VideoTexture(this.video);
1119
+ this.texture.minFilter = THREE.LinearFilter;
1120
+ this.texture.magFilter = THREE.LinearFilter;
1121
+ this.texture.format = THREE.RGBFormat;
1122
+
1123
+ var geometry = new THREE.PlaneGeometry(1, 1);
1124
+ geometry.scale(1, 480 / 720, 1);
1125
+ geometry.translate(0, 0.33, 0);
1126
+ this.material = new THREE.MeshBasicMaterial({
1127
+ map: this.texture,
1128
+ side: THREE.DoubleSide,
1129
+ });
1130
+ var mesh = new THREE.Mesh(geometry, this.material);
1131
+ this.outputs.objectRoot = mesh;
1132
+ this.outputs.collider = mesh;
1133
+ mesh.visible = this.inputs.visible;
1134
+
1135
+ this.outputs.texture = this.texture;
1136
+ this.outputs.video = this.video;
1137
+ };
1138
+
1139
+ this.releaseTexture = function () {
1140
+ if (this.texture) {
1141
+ this.outputs.texture = null;
1142
+ this.texture.dispose();
1143
+ }
1144
+ };
1145
+
1146
+ this.createVideoElement = function () {
1147
+ const video = document.createElement("video");
1148
+ video.setAttribute("id", "htmlLivestreamVideo");
1149
+ video.crossOrigin = "anonymous";
1150
+ video.setAttribute("height", "480");
1151
+ video.setAttribute("width", "720");
1152
+ video.setAttribute("webkit-playsinline", "webkit-playsinline");
1153
+ video.setAttribute("controls", "controls");
1154
+ video.muted = false;
1155
+ video.loop = true;
1156
+ video.volume = 1;
1157
+ return video;
1158
+ };
1159
+
1160
+ this.onTick = function (tickDelta) {};
1161
+
1162
+ this.onDestroy = function () {
1163
+ this.material.dispose();
1164
+ };
1165
+
1166
+ this.spyOnEvent = function (payload) {
1167
+ console.log("payload", payload);
1168
+ };
1169
+ }
1170
+
1171
+ function makeVideoRender() {
1172
+ return new videoRenderer();
1173
+ }
1174
+
1175
+ _atwin.Scene.register("liveVideo", makeVideoRender);
1176
+ }
1177
+
1178
+ //This function is for registering the custom component
1179
+ function createSlideScreenModel() {
1180
+ function SlideScreenModelRenderer() {
1181
+ this.inputs = {
1182
+ src: null,
1183
+ userNavigationEnabled: true,
1184
+ eventsEnabled: true,
1185
+ colliderEnabled: true,
1186
+ visible: true,
1187
+ };
1188
+ this.outputs = {
1189
+ texture: null,
1190
+ t_image: null,
1191
+ };
1192
+ this.onInit = function () {
1193
+ this.texture;
1194
+ this.t_image;
1195
+ this.material;
1196
+ this.mesh;
1197
+ };
1198
+
1199
+ //This is commented out since the slideshow must not be clicked yet
1200
+
1201
+ this.events = {
1202
+ "INTERACTION.CLICK": true,
1203
+ };
1204
+
1205
+ this.onEvent = async function (eventType, eventData) {
1206
+ console.log("onEventSlideShow", eventType, eventData);
1207
+ if (eventType == "INTERACTION.CLICK") {
1208
+ if (_allSlideShow.length > 0) {
1209
+ let slideshow = _allSlideShow.filter(
1210
+ (l) =>
1211
+ l.collider?.collider?.uuid == eventData.collider?.uuid ?? false
1212
+ )[0];
1213
+ console.log("slideShow eventData", slideshow);
1214
+ if (slideshow != null) {
1215
+ console.log("slideShow eventData", slideshow);
1216
+
1217
+ // This is the UNIQUE properties of this plane
1218
+ var mousePosition = await getMousePosition();
1219
+
1220
+ // @ts-ignore
1221
+ var planePosition = slideshow.node.obj3D.position;
1222
+
1223
+ console.log("MOUSE POSITION", mousePosition);
1224
+ console.log("PLANE POSITION", planePosition);
1225
+
1226
+ if (slideshow.object.object_type == "ZIP") {
1227
+ if (slideshow.id != _currentSlideShowID) {
1228
+ _currentSlideIndex = _slideShowImage.find(
1229
+ (elem) => elem.id === slideshow.id
1230
+ ).idx;
1231
+ console.log("CURRENT INDEX", _currentSlideIndex);
1232
+ _currentSlideShow = _slideShowImage.find(
1233
+ (elem) => elem.id === slideshow.id
1234
+ ).images;
1235
+ console.log("CURRENT SLIDE", _currentSlideShow);
1236
+ }
1237
+ if (slideshow.id == _currentSlideShowID) {
1238
+ console.log("ChangeImage", slideshow, _currentSlideShowID);
1239
+ if (planePosition.x < mousePosition.x) {
1240
+ console.log("LEFT SIDE");
1241
+ clearInterval(_timer);
1242
+ // model.pause()
1243
+ this.inputs.src = imageStream("prev");
1244
+ } else if (planePosition.y < mousePosition.y) {
1245
+ console.log("RIGHT SIDE");
1246
+ // model.pause()
1247
+ clearInterval(_timer);
1248
+ this.inputs.src = imageStream("next");
1249
+ } else if (planePosition.y > mousePosition.y) {
1250
+ console.log("AUTOPLAY");
1251
+ _timer = setInterval(() => {
1252
+ _currentSlideIndex =
1253
+ (_currentSlideIndex + 1) % _currentSlideShow.length;
1254
+ console.log("CURRENT INDEX", _currentSlideIndex);
1255
+ this.inputs.src = imageStream("next");
1256
+ }, 2000);
1257
+ }
1258
+ } else {
1259
+ _currentSlideShowID = slideshow.id;
1260
+ }
1261
+ }
1262
+ }
1263
+ }
1264
+ }
1265
+ };
1266
+
1267
+ this.onInputsUpdated = function (previous) {
1268
+ this.releaseTexture();
1269
+ const three = this.context.three;
1270
+ // Create IMAGE
1271
+ this.t_image = document.createElement("IMG");
1272
+ this.t_image.src = this.inputs.src;
1273
+ this.t_image.setAttribute("width", "auto");
1274
+ this.t_image.setAttribute("height", "auto");
1275
+ this.t_image.crossOrigin = "anonymous";
1276
+ const planeGeo = new three.PlaneGeometry(1.5, 1.5);
1277
+ // planeGeo.scale(1, 480/720, 1);
1278
+ planeGeo.translate(0, 0.33, 0);
1279
+
1280
+ // this.texture = new THREE.Texture(this.t_image);
1281
+
1282
+ // used texture loader for onLoad callback
1283
+ if (previous.src != this.inputs.src) {
1284
+ console.log("this.inputs.src", this.inputs.src);
1285
+ this.texture = new three.TextureLoader().load(
1286
+ this.t_image.src,
1287
+ (tex = this.texture) => {
1288
+ // onLoad callback function
1289
+ tex.minFilter = three.LinearFilter;
1290
+ tex.magFilter = three.LinearFilter;
1291
+ tex.format = three.RGBAFormat;
1292
+ tex.needsUpdate = true; //This should be true in order to update the texture
1293
+ tex.onUpdate = this.textureUpdated;
1294
+ this.material = new three.MeshBasicMaterial({
1295
+ map: this.texture,
1296
+ side: three.DoubleSide,
1297
+ transparent: true,
1298
+ // color: 0xFF0000,
1299
+ alphaTest: 0.5,
1300
+ });
1301
+ this.mesh = new three.Mesh(planeGeo, this.material); //Create a mesh for the Plane
1302
+ this.mesh.scale.set(
1303
+ 1.0,
1304
+ this.texture.image.height / this.texture.image.width,
1305
+ 1.0
1306
+ ); // setting the aspect ratio of image as the scale of mesh
1307
+
1308
+ this.outputs.objectRoot = this.mesh;
1309
+ this.outputs.collider = this.mesh;
1310
+ this.outputs.texture = this.texture;
1311
+ this.outputs.t_image = this.t_image;
1312
+ }
1313
+ );
1314
+ }
1315
+ if (this.mesh != null) {
1316
+ this.mesh.visible = this.inputs.visible;
1317
+ }
1318
+ };
1319
+
1320
+ this.textureUpdated = function (params) {
1321
+ console.log("Update Callback", params);
1322
+ };
1323
+
1324
+ this.releaseTexture = function () {
1325
+ if (this.texture) {
1326
+ this.outputs.texture = null;
1327
+ }
1328
+ };
1329
+
1330
+ this.onTick = function (tickDelta) {};
1331
+
1332
+ this.onDestroy = function () {
1333
+ this.material.dispose();
1334
+ this.texture.dispose();
1335
+ };
1336
+ }
1337
+
1338
+ function makeSlideScreenModelRenderer() {
1339
+ return new SlideScreenModelRenderer();
1340
+ }
1341
+
1342
+ // Registering the component with the sdk
1343
+ _atwin.Scene.register("slideScreenModel", makeSlideScreenModelRenderer);
1344
+ }
1345
+
1346
+ //This is the function for rendering the slidescreen model
1347
+ async function showSlideScreenModel(object: IShowcaseObject) {
1348
+ createSlideScreenModel();
1349
+
1350
+ const [sceneObject] = await _atwin.Scene.createObjects(1);
1351
+ const modelNode = sceneObject.addNode();
1352
+ const component = modelNode.addComponent("slideScreenModel");
1353
+ console.log("COMPONENT", component);
1354
+
1355
+ modelNode.scale.set = {
1356
+ x: object.object_scale.x,
1357
+ y: object.object_scale.y,
1358
+ z: object.object_scale.z,
1359
+ };
1360
+
1361
+ //@ts-ignore
1362
+ modelNode.obj3D.position.set(
1363
+ object.object_position.x,
1364
+ object.object_position.y,
1365
+ object.object_position.z
1366
+ );
1367
+
1368
+ //@ts-ignore
1369
+ modelNode.obj3D.rotation.set(
1370
+ object.object_rotation.x,
1371
+ object.object_rotation.y,
1372
+ object.object_rotation.z
1373
+ );
1374
+
1375
+ // modelNode.scale.set(1, 1, 1)
1376
+ // modelNode.position.set(-3.32,0.01 ,3.33)
1377
+ modelNode.start();
1378
+
1379
+ await addImageToSlideShow(object.object_data, component, object.id);
1380
+ console.log("SLIDE OUTPUT", component.outputs);
1381
+
1382
+ var slideShow = {
1383
+ id: object.id,
1384
+ collider: component.outputs,
1385
+ object: object.object_data,
1386
+ node: modelNode,
1387
+ component: component,
1388
+ type: 'ZIP'
1389
+ } as ISlideshowObject
1390
+
1391
+ _allSlideShow.push(slideShow);
1392
+ console.log("ALL SLIDESHOW", _allSlideShow);
1393
+ }
1394
+
1395
+ //This function is accessing the zip files and place it inside the slidescreen
1396
+ async function addImageToSlideShow(data, component, objectID) {
1397
+ var dataList = [];
1398
+ await new JSZip.external.Promise(function (resolve, reject) {
1399
+ JSZipUtils.getBinaryContent(data.amazon_uri, function (err, data) {
1400
+ if (err) {
1401
+ console.log("getBI error", err);
1402
+ reject(err);
1403
+ } else {
1404
+ console.log("getBI success", data);
1405
+ resolve(data);
1406
+ }
1407
+ });
1408
+ })
1409
+ .then(function (data) {
1410
+ console.log("data from getBI", data);
1411
+ //@ts-ignore
1412
+ return JSZip.loadAsync(data);
1413
+ })
1414
+ .then((data) => {
1415
+ console.log("data from loadAsync", data);
1416
+ for (let key in data.files) {
1417
+ // determine whether it is a directory
1418
+ if (key.includes("__MACOSX")) continue;
1419
+ if (key.includes(".DS_Store")) continue;
1420
+ if (data.files[key].dir) continue;
1421
+ let base = data.file(data.files[key].name).async("base64"); // will be converted to base64 format picture
1422
+ base.then((res) => {
1423
+ dataList.push({
1424
+ name: data.files[key].name,
1425
+ path: `data:image/png;base64,${res}`,
1426
+ });
1427
+ if (dataList.length == 1) {
1428
+ component.inputs.src = `data:image/png;base64,${res}`;
1429
+ }
1430
+ });
1431
+ }
1432
+
1433
+ _slideShowImage.push({
1434
+ id: objectID,
1435
+ images: dataList,
1436
+ idx: 0,
1437
+ playID: null,
1438
+ play: false,
1439
+ comp: component,
1440
+ });
1441
+ });
1442
+ }
1443
+
1444
+ async function getMousePosition() {
1445
+ var planePosition;
1446
+ await _atwin.Pointer.intersection.subscribe(function (intersection) {
1447
+ planePosition = intersection.position;
1448
+ // console.log("This is intersection mouse position", planePosition);
1449
+ });
1450
+ // pointer.cancel();
1451
+ return planePosition;
1452
+ }
1453
+
1454
+ //This is the function to change the image in the slideshow
1455
+ function imageStream(direction) {
1456
+ let diff = direction === "prev" ? -1 : 1;
1457
+ let length = _currentSlideShow.length;
1458
+ let index = _currentSlideIndex + diff;
1459
+ if (index === -1) {
1460
+ index = length - 1;
1461
+ }
1462
+ if (index === length) {
1463
+ index = 0;
1464
+ }
1465
+
1466
+ _currentSlideIndex = index;
1467
+ _slideShowImage.find((elem) => elem.id === _currentSlideShowID).idx = index;
1468
+ return _currentSlideShow[_currentSlideIndex].path;
1469
+ }
1470
+
1471
+ // ===== END OBJECTS FUNCTIONALITY =====
1472
+
1473
+ // ===== NAVIGATION AND CAMERA CONTROL =====
1474
+ async function cameraRotate(x: number, y: number, speed: number) {
1475
+ // x and y is rotation in horizontal and vertical rotation in degrees
1476
+ // speed, speed of rotation
1477
+ await _atwin.Camera.rotate(x, y, { speed: speed })
1478
+ .then(() => {
1479
+ console.log("camera rotate success.", x, y);
1480
+ })
1481
+ .catch((error) => {
1482
+ console.log("camera rotate error.", error);
1483
+ });
1484
+ }
1485
+
1486
+ async function cameraPan(x: number, z: number) {
1487
+ let mode = getViewMode();
1488
+
1489
+ if (
1490
+ mode == _atwin.Mode.Mode.FLOORPLAN ||
1491
+ mode == _atwin.Mode.Mode.DOLLHOUSE
1492
+ ) {
1493
+ // x is pan (look sideways left and right) in degrees
1494
+ // z is tilt (look up and down) in degrees
1495
+ await _atwin.Camera.pan({ x: x, z: z })
1496
+ .then(() => {
1497
+ console.log("camera pan success.");
1498
+ })
1499
+ .catch((error) => {
1500
+ console.log("camera rotate error:", error);
1501
+ });
1502
+ } else {
1503
+ console.error("Incorrect view mode.");
1504
+ }
1505
+ }
1506
+
1507
+ async function cameraLookAt(x: number, y: number) {
1508
+ // set camera view at coordinate in SCREEN
1509
+ // x and y is PIXEL from upper left corner
1510
+ await _atwin.Camera.lookAtScreenCoords(x, y)
1511
+ .then(function () {
1512
+ console.log("camera looking at...", x, y);
1513
+ })
1514
+ .catch(function (error) {
1515
+ console.log("camera looking at error:", error);
1516
+ });
1517
+ }
1518
+
1519
+ /**
1520
+ * Moves the camera position in the direction of the argument provided.
1521
+ * @constructor
1522
+ * @param direction FORWARD,BACKWARD,LEFT,RIGHT,UP,DOWN
1523
+ * @type {string}
1524
+ */
1525
+
1526
+ async function moveInDirection(direction: string) {
1527
+ // move in DIRECTION, can be LEFT, RIGTH, UP, DOWN, FORWARD, BACKWARD
1528
+ let nextDirection = _atwin.Camera.Direction.LEFT;
1529
+ switch (direction.toUpperCase()) {
1530
+ case "LEFT":
1531
+ nextDirection = _atwin.Camera.Direction.LEFT;
1532
+
1533
+ break;
1534
+ case "RIGHT":
1535
+ nextDirection = _atwin.Camera.Direction.RIGHT;
1536
+
1537
+ break;
1538
+
1539
+ case "UP":
1540
+ nextDirection = _atwin.Camera.Direction.UP;
1541
+
1542
+ break;
1543
+ case "DOWN":
1544
+ nextDirection = _atwin.Camera.Direction.DOWN;
1545
+
1546
+ break;
1547
+
1548
+ case "BACK":
1549
+ nextDirection = _atwin.Camera.Direction.BACK;
1550
+
1551
+ break;
1552
+
1553
+ case "FORWARD":
1554
+ nextDirection = _atwin.Camera.Direction.FORWARD;
1555
+
1556
+ break;
1557
+
1558
+ default:
1559
+ nextDirection = _atwin.Camera.Direction.LEFT;
1560
+
1561
+ break;
1562
+ }
1563
+
1564
+ await _atwin.Camera.moveInDirection(nextDirection)
1565
+ .then(function () {
1566
+ console.log("Move to...", nextDirection);
1567
+ })
1568
+ .catch(function () {
1569
+ console.warn(
1570
+ "An error occured while moving in that direction.",
1571
+ nextDirection
1572
+ );
1573
+ });
1574
+ }
1575
+
1576
+ // =========================================== END NAVIGATION AND CAMERA CONTROL ===================================
1577
+
1578
+ // =========================================== MODE FUNCTIONALITY ==================================================
1579
+ function getViewMode() {
1580
+ return _currentViewMode;
1581
+ }
1582
+
1583
+ async function setViewMode(mode: string) {
1584
+ console.log("=== get Mode Type ===", _getModeType(mode));
1585
+ let modeType = _getModeType(mode);
1586
+
1587
+ await _atwin.Mode.moveTo(modeType)
1588
+ .then(function (nextMode) {
1589
+ // Move successful.
1590
+ console.log("Arrived at new view mode " + nextMode);
1591
+ })
1592
+ .catch(function (error) {
1593
+ // Error with moveTo command
1594
+ console.error("Error occur on:", error);
1595
+ });
1596
+ }
1597
+
1598
+ function _getModeType(mode: string) {
1599
+ let modes = {
1600
+ DOLLHOUSE: "mode.dollhouse",
1601
+ FLOORPLAN: "mode.floorplan",
1602
+ INSIDE: "mode.inside",
1603
+ OUTSIDE: "mode.outside",
1604
+ TRANSITIONING: "mode.transitioning",
1605
+ };
1606
+
1607
+ return modes[mode.toUpperCase()] || "";
1608
+ }
1609
+
1610
+ // ================== INPUT EVENT CLASSES ======================= //
1611
+
1612
+ class ClickSpy {
1613
+ object_data: IShowcaseObject;
1614
+ node: Scene.INode;
1615
+ component: Scene.IComponent;
1616
+
1617
+ constructor(data:IShowcaseObject,node:Scene.INode,component:Scene.IComponent){
1618
+ this.object_data = data
1619
+ this.node = node
1620
+ this.component = component
1621
+ }
1622
+ public eventType = 'INTERACTION.CLICK';
1623
+ public async onEvent(payload: unknown) {
1624
+ console.log('INTERACTION.CLICK', payload);
1625
+ const selectedObj:IObjectData = {
1626
+ object: this.object_data,
1627
+ node: this.node,
1628
+ component: this.component
1629
+ }
1630
+
1631
+ setSelectedObject(this.object_data,this.node,this.component)
1632
+ await setTransformControls(selectedObj)
1633
+ }
1634
+ }
1635
+
1636
+ export {
1637
+ tags,
1638
+ sweeps,
1639
+ connectSpace,
1640
+ gotoTag,
1641
+ getCurrentSweep,
1642
+ getCurrentSweepPosition,
1643
+ moveToSweep,
1644
+ getNearbySweeps,
1645
+ pauseVideo,
1646
+ playVideo,
1647
+ getCurrentCameraPose,
1648
+ getCameraPosition,
1649
+ moveInDirection,
1650
+ cameraLookAt,
1651
+ cameraPan,
1652
+ cameraRotate,
1653
+ getViewMode,
1654
+ setViewMode,
1655
+ };
1656
+