@multisetai/vps 1.0.4
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.
- package/LICENSE +21 -0
- package/README.md +377 -0
- package/dist/core/index.d.ts +201 -0
- package/dist/core/index.js +152 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +398 -0
- package/dist/index.js.map +1 -0
- package/dist/webxr/index.d.ts +32 -0
- package/dist/webxr/index.js +252 -0
- package/dist/webxr/index.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
|
|
3
|
+
// src/lib/core/index.ts
|
|
4
|
+
var DEFAULT_ENDPOINTS = {
|
|
5
|
+
authUrl: "https://api.multiset.ai/v1/m2m/token",
|
|
6
|
+
queryUrl: "https://api.multiset.ai/v1/vps/map/query-form",
|
|
7
|
+
mapDetailsUrl: "https://api.multiset.ai/v1/vps/map/",
|
|
8
|
+
mapSetDetailsUrl: "https://api.multiset.ai/v1/vps/map-set/",
|
|
9
|
+
fileDownloadUrl: "https://api.multiset.ai/v1/file"
|
|
10
|
+
};
|
|
11
|
+
var MultisetClient = class {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
this.accessToken = null;
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.endpoints = {
|
|
17
|
+
...DEFAULT_ENDPOINTS,
|
|
18
|
+
...config.endpoints
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
get token() {
|
|
22
|
+
return this.accessToken;
|
|
23
|
+
}
|
|
24
|
+
async authorize() {
|
|
25
|
+
var _a, _b, _c, _d, _e;
|
|
26
|
+
try {
|
|
27
|
+
const response = await axios.post(
|
|
28
|
+
this.endpoints.authUrl,
|
|
29
|
+
{},
|
|
30
|
+
{
|
|
31
|
+
auth: {
|
|
32
|
+
username: this.config.clientId,
|
|
33
|
+
password: this.config.clientSecret
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
const token = (_c = (_a = response.data) == null ? void 0 : _a.token) != null ? _c : (_b = response.data) == null ? void 0 : _b.access_token;
|
|
38
|
+
if (!token) {
|
|
39
|
+
throw new Error("Authorization succeeded but no token was returned.");
|
|
40
|
+
}
|
|
41
|
+
this.accessToken = token;
|
|
42
|
+
(_e = (_d = this.config).onAuthorize) == null ? void 0 : _e.call(_d, token);
|
|
43
|
+
return token;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
this.handleError(error);
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
handleError(error) {
|
|
50
|
+
var _a, _b, _c, _d;
|
|
51
|
+
if (axios.isAxiosError(error)) {
|
|
52
|
+
const axiosError = error;
|
|
53
|
+
(_b = (_a = this.config).onError) == null ? void 0 : _b.call(_a, axiosError);
|
|
54
|
+
} else {
|
|
55
|
+
(_d = (_c = this.config).onError) == null ? void 0 : _d.call(_c, error);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async localizeWithFrame(frame, intrinsics) {
|
|
59
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
60
|
+
if (!this.accessToken) {
|
|
61
|
+
throw new Error("Access token is missing. Call authorize() first.");
|
|
62
|
+
}
|
|
63
|
+
(_b = (_a = this.config).onFrameCaptured) == null ? void 0 : _b.call(_a, frame);
|
|
64
|
+
(_d = (_c = this.config).onCameraIntrinsics) == null ? void 0 : _d.call(_c, intrinsics);
|
|
65
|
+
const queryResult = await this.queryLocalization(frame, intrinsics);
|
|
66
|
+
if ((_e = queryResult == null ? void 0 : queryResult.localizeData) == null ? void 0 : _e.poseFound) {
|
|
67
|
+
(_g = (_f = this.config).onPoseResult) == null ? void 0 : _g.call(_f, queryResult.localizeData);
|
|
68
|
+
}
|
|
69
|
+
return queryResult;
|
|
70
|
+
}
|
|
71
|
+
async queryLocalization(frame, intrinsics) {
|
|
72
|
+
var _a;
|
|
73
|
+
const formData = new FormData();
|
|
74
|
+
formData.append("isRightHanded", "true");
|
|
75
|
+
formData.append("width", `${frame.width}`);
|
|
76
|
+
formData.append("height", `${frame.height}`);
|
|
77
|
+
formData.append("px", `${intrinsics.px}`);
|
|
78
|
+
formData.append("py", `${intrinsics.py}`);
|
|
79
|
+
formData.append("fx", `${intrinsics.fx}`);
|
|
80
|
+
formData.append("fy", `${intrinsics.fy}`);
|
|
81
|
+
formData.append("queryImage", frame.blob);
|
|
82
|
+
if (this.config.mapType === "map") {
|
|
83
|
+
formData.append("mapCode", this.config.code);
|
|
84
|
+
} else {
|
|
85
|
+
formData.append("mapSetCode", this.config.code);
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const response = await axios.post(
|
|
89
|
+
this.endpoints.queryUrl,
|
|
90
|
+
formData,
|
|
91
|
+
{
|
|
92
|
+
headers: {
|
|
93
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
const data = response.data;
|
|
98
|
+
if (!data.poseFound) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const result = {
|
|
102
|
+
localizeData: data
|
|
103
|
+
};
|
|
104
|
+
if ((_a = data.mapIds) == null ? void 0 : _a.length) {
|
|
105
|
+
const mapDetails = await this.fetchMapDetails(data.mapIds[0]);
|
|
106
|
+
if (mapDetails) {
|
|
107
|
+
result.mapDetails = mapDetails;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
this.handleError(error);
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async fetchMapDetails(mapId) {
|
|
117
|
+
try {
|
|
118
|
+
const response = await axios.get(
|
|
119
|
+
`${this.endpoints.mapDetailsUrl}${mapId}`,
|
|
120
|
+
{
|
|
121
|
+
headers: {
|
|
122
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
return response.data;
|
|
127
|
+
} catch (error) {
|
|
128
|
+
this.handleError(error);
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async fetchMapSetDetails(mapSetId) {
|
|
133
|
+
try {
|
|
134
|
+
const response = await axios.get(
|
|
135
|
+
`${this.endpoints.mapSetDetailsUrl}${mapSetId}`,
|
|
136
|
+
{
|
|
137
|
+
headers: {
|
|
138
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
return response.data;
|
|
143
|
+
} catch (error) {
|
|
144
|
+
this.handleError(error);
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export { DEFAULT_ENDPOINTS, MultisetClient };
|
|
151
|
+
//# sourceMappingURL=index.js.map
|
|
152
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/core/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import axios, { AxiosError } from 'axios';\nimport type {\n ILocalizeAndMapDetails,\n ILocalizeResponse,\n MapType,\n IGetMapsDetailsResponse,\n IMapSetMapsResponse,\n} from './types';\n\nexport interface IMultisetSdkConfig {\n clientId: string;\n clientSecret: string;\n code: string;\n mapType: MapType;\n endpoints?: Partial<IMultisetSdkEndpoints>;\n onAuthorize?: (token: string) => void;\n onFrameCaptured?: (payload: IFrameCaptureEvent) => void;\n onCameraIntrinsics?: (intrinsics: ICameraIntrinsicsEvent) => void;\n onPoseResult?: (payload: IPoseResultEvent) => void;\n onError?: (error: unknown) => void;\n}\n\nexport interface IMultisetSdkEndpoints {\n authUrl: string;\n queryUrl: string;\n mapDetailsUrl: string;\n fileDownloadUrl: string;\n mapSetDetailsUrl: string;\n}\n\nexport interface IFrameCaptureEvent {\n blob: Blob;\n width: number;\n height: number;\n}\n\nexport interface ICameraIntrinsicsEvent {\n fx: number;\n fy: number;\n px: number;\n py: number;\n width: number;\n height: number;\n}\n\nexport interface IPoseResultEvent {\n poseFound: boolean;\n position: {\n x: number;\n y: number;\n z: number;\n };\n rotation: {\n x: number;\n y: number;\n z: number;\n w: number;\n };\n mapIds: string[];\n confidence?: number;\n}\n\nexport interface ILocalizeResultEvent {\n frame: IFrameCaptureEvent;\n intrinsics: ICameraIntrinsicsEvent;\n response: ILocalizeAndMapDetails | null;\n}\n\nexport const DEFAULT_ENDPOINTS: IMultisetSdkEndpoints = {\n authUrl: 'https://api.multiset.ai/v1/m2m/token',\n queryUrl: 'https://api.multiset.ai/v1/vps/map/query-form',\n mapDetailsUrl: 'https://api.multiset.ai/v1/vps/map/',\n mapSetDetailsUrl: 'https://api.multiset.ai/v1/vps/map-set/',\n fileDownloadUrl: 'https://api.multiset.ai/v1/file',\n};\n\n/**\n * Placeholder class to be implemented by porting logic from multiset-webxr-sdk.\n */\nexport class MultisetClient {\n private readonly endpoints: IMultisetSdkEndpoints;\n private accessToken: string | null = null;\n\n constructor(private readonly config: IMultisetSdkConfig) {\n this.config = config;\n this.endpoints = {\n ...DEFAULT_ENDPOINTS,\n ...config.endpoints,\n };\n }\n\n get token(): string | null {\n return this.accessToken;\n }\n\n async authorize(): Promise<string> {\n try {\n const response = await axios.post(\n this.endpoints.authUrl,\n {},\n {\n auth: {\n username: this.config.clientId,\n password: this.config.clientSecret,\n },\n }\n );\n\n const token: string | undefined =\n response.data?.token ?? response.data?.access_token;\n\n if (!token) {\n throw new Error('Authorization succeeded but no token was returned.');\n }\n\n this.accessToken = token;\n this.config.onAuthorize?.(token);\n return token;\n } catch (error) {\n this.handleError(error);\n throw error;\n }\n }\n\n private handleError(error: unknown): void {\n if (axios.isAxiosError(error)) {\n const axiosError = error as AxiosError;\n this.config.onError?.(axiosError);\n } else {\n this.config.onError?.(error);\n }\n }\n\n async localizeWithFrame(\n frame: IFrameCaptureEvent,\n intrinsics: ICameraIntrinsicsEvent\n ): Promise<ILocalizeAndMapDetails | null> {\n if (!this.accessToken) {\n throw new Error('Access token is missing. Call authorize() first.');\n }\n\n this.config.onFrameCaptured?.(frame);\n this.config.onCameraIntrinsics?.(intrinsics);\n\n const queryResult = await this.queryLocalization(frame, intrinsics);\n\n if (queryResult?.localizeData?.poseFound) {\n this.config.onPoseResult?.(queryResult.localizeData);\n }\n\n return queryResult;\n }\n\n private async queryLocalization(\n frame: IFrameCaptureEvent,\n intrinsics: ICameraIntrinsicsEvent\n ): Promise<ILocalizeAndMapDetails | null> {\n const formData = new FormData();\n formData.append('isRightHanded', 'true');\n formData.append('width', `${frame.width}`);\n formData.append('height', `${frame.height}`);\n formData.append('px', `${intrinsics.px}`);\n formData.append('py', `${intrinsics.py}`);\n formData.append('fx', `${intrinsics.fx}`);\n formData.append('fy', `${intrinsics.fy}`);\n formData.append('queryImage', frame.blob);\n\n if (this.config.mapType === 'map') {\n formData.append('mapCode', this.config.code);\n } else {\n formData.append('mapSetCode', this.config.code);\n }\n\n try {\n const response = await axios.post<ILocalizeResponse>(\n this.endpoints.queryUrl,\n formData,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n\n const data = response.data;\n if (!data.poseFound) {\n return null;\n }\n\n const result: ILocalizeAndMapDetails = {\n localizeData: data,\n };\n\n if (data.mapIds?.length) {\n const mapDetails = await this.fetchMapDetails(data.mapIds[0]);\n if (mapDetails) {\n result.mapDetails = mapDetails;\n }\n }\n\n return result;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n\n private async fetchMapDetails(mapId: string): Promise<IGetMapsDetailsResponse | null> {\n try {\n const response = await axios.get<IGetMapsDetailsResponse>(\n `${this.endpoints.mapDetailsUrl}${mapId}`,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n return response.data;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n\n private async fetchMapSetDetails(mapSetId: string): Promise<IMapSetMapsResponse | null> {\n try {\n const response = await axios.get<IMapSetMapsResponse>(\n `${this.endpoints.mapSetDetailsUrl}${mapSetId}`,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n return response.data;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n}\n\nexport type { IMultisetSdkConfig as IMultisetClientOptions };\nexport type {\n MapType,\n ILocalizeResponse,\n ILocalizeAndMapDetails,\n IGetMapsDetailsResponse,\n IMapSetMapsResponse,\n} from './types';\n\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { DEFAULT_ENDPOINTS, ICameraIntrinsicsEvent, IFrameCaptureEvent, IGetMapsDetailsResponse, ILocalizeAndMapDetails, ILocalizeResponse, ILocalizeResultEvent, IMapSetMapsResponse, IMultisetClientOptions, IMultisetClientOptions as IMultisetSdkConfig, IMultisetSdkEndpoints, IPoseResultEvent, MapType, MultisetClient } from './core/index.js';
|
|
2
|
+
export { IWebxrControllerOptions, WebxrController } from './webxr/index.js';
|
|
3
|
+
import 'three';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { ARButton } from 'three/examples/jsm/webxr/ARButton.js';
|
|
4
|
+
|
|
5
|
+
// src/lib/core/index.ts
|
|
6
|
+
var DEFAULT_ENDPOINTS = {
|
|
7
|
+
authUrl: "https://api.multiset.ai/v1/m2m/token",
|
|
8
|
+
queryUrl: "https://api.multiset.ai/v1/vps/map/query-form",
|
|
9
|
+
mapDetailsUrl: "https://api.multiset.ai/v1/vps/map/",
|
|
10
|
+
mapSetDetailsUrl: "https://api.multiset.ai/v1/vps/map-set/",
|
|
11
|
+
fileDownloadUrl: "https://api.multiset.ai/v1/file"
|
|
12
|
+
};
|
|
13
|
+
var MultisetClient = class {
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.accessToken = null;
|
|
17
|
+
this.config = config;
|
|
18
|
+
this.endpoints = {
|
|
19
|
+
...DEFAULT_ENDPOINTS,
|
|
20
|
+
...config.endpoints
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
get token() {
|
|
24
|
+
return this.accessToken;
|
|
25
|
+
}
|
|
26
|
+
async authorize() {
|
|
27
|
+
var _a, _b, _c, _d, _e;
|
|
28
|
+
try {
|
|
29
|
+
const response = await axios.post(
|
|
30
|
+
this.endpoints.authUrl,
|
|
31
|
+
{},
|
|
32
|
+
{
|
|
33
|
+
auth: {
|
|
34
|
+
username: this.config.clientId,
|
|
35
|
+
password: this.config.clientSecret
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
const token = (_c = (_a = response.data) == null ? void 0 : _a.token) != null ? _c : (_b = response.data) == null ? void 0 : _b.access_token;
|
|
40
|
+
if (!token) {
|
|
41
|
+
throw new Error("Authorization succeeded but no token was returned.");
|
|
42
|
+
}
|
|
43
|
+
this.accessToken = token;
|
|
44
|
+
(_e = (_d = this.config).onAuthorize) == null ? void 0 : _e.call(_d, token);
|
|
45
|
+
return token;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
this.handleError(error);
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
handleError(error) {
|
|
52
|
+
var _a, _b, _c, _d;
|
|
53
|
+
if (axios.isAxiosError(error)) {
|
|
54
|
+
const axiosError = error;
|
|
55
|
+
(_b = (_a = this.config).onError) == null ? void 0 : _b.call(_a, axiosError);
|
|
56
|
+
} else {
|
|
57
|
+
(_d = (_c = this.config).onError) == null ? void 0 : _d.call(_c, error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async localizeWithFrame(frame, intrinsics) {
|
|
61
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
62
|
+
if (!this.accessToken) {
|
|
63
|
+
throw new Error("Access token is missing. Call authorize() first.");
|
|
64
|
+
}
|
|
65
|
+
(_b = (_a = this.config).onFrameCaptured) == null ? void 0 : _b.call(_a, frame);
|
|
66
|
+
(_d = (_c = this.config).onCameraIntrinsics) == null ? void 0 : _d.call(_c, intrinsics);
|
|
67
|
+
const queryResult = await this.queryLocalization(frame, intrinsics);
|
|
68
|
+
if ((_e = queryResult == null ? void 0 : queryResult.localizeData) == null ? void 0 : _e.poseFound) {
|
|
69
|
+
(_g = (_f = this.config).onPoseResult) == null ? void 0 : _g.call(_f, queryResult.localizeData);
|
|
70
|
+
}
|
|
71
|
+
return queryResult;
|
|
72
|
+
}
|
|
73
|
+
async queryLocalization(frame, intrinsics) {
|
|
74
|
+
var _a;
|
|
75
|
+
const formData = new FormData();
|
|
76
|
+
formData.append("isRightHanded", "true");
|
|
77
|
+
formData.append("width", `${frame.width}`);
|
|
78
|
+
formData.append("height", `${frame.height}`);
|
|
79
|
+
formData.append("px", `${intrinsics.px}`);
|
|
80
|
+
formData.append("py", `${intrinsics.py}`);
|
|
81
|
+
formData.append("fx", `${intrinsics.fx}`);
|
|
82
|
+
formData.append("fy", `${intrinsics.fy}`);
|
|
83
|
+
formData.append("queryImage", frame.blob);
|
|
84
|
+
if (this.config.mapType === "map") {
|
|
85
|
+
formData.append("mapCode", this.config.code);
|
|
86
|
+
} else {
|
|
87
|
+
formData.append("mapSetCode", this.config.code);
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const response = await axios.post(
|
|
91
|
+
this.endpoints.queryUrl,
|
|
92
|
+
formData,
|
|
93
|
+
{
|
|
94
|
+
headers: {
|
|
95
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
const data = response.data;
|
|
100
|
+
if (!data.poseFound) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const result = {
|
|
104
|
+
localizeData: data
|
|
105
|
+
};
|
|
106
|
+
if ((_a = data.mapIds) == null ? void 0 : _a.length) {
|
|
107
|
+
const mapDetails = await this.fetchMapDetails(data.mapIds[0]);
|
|
108
|
+
if (mapDetails) {
|
|
109
|
+
result.mapDetails = mapDetails;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
this.handleError(error);
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async fetchMapDetails(mapId) {
|
|
119
|
+
try {
|
|
120
|
+
const response = await axios.get(
|
|
121
|
+
`${this.endpoints.mapDetailsUrl}${mapId}`,
|
|
122
|
+
{
|
|
123
|
+
headers: {
|
|
124
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
return response.data;
|
|
129
|
+
} catch (error) {
|
|
130
|
+
this.handleError(error);
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async fetchMapSetDetails(mapSetId) {
|
|
135
|
+
try {
|
|
136
|
+
const response = await axios.get(
|
|
137
|
+
`${this.endpoints.mapSetDetailsUrl}${mapSetId}`,
|
|
138
|
+
{
|
|
139
|
+
headers: {
|
|
140
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
return response.data;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
this.handleError(error);
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
var WebxrController = class {
|
|
152
|
+
constructor(options) {
|
|
153
|
+
this.options = options;
|
|
154
|
+
this.renderer = null;
|
|
155
|
+
this.camera = null;
|
|
156
|
+
this.scene = null;
|
|
157
|
+
this.animationLoop = null;
|
|
158
|
+
this.arButton = null;
|
|
159
|
+
this.resizeHandler = null;
|
|
160
|
+
this.isSessionActive = false;
|
|
161
|
+
}
|
|
162
|
+
async initialize(buttonContainer) {
|
|
163
|
+
var _a, _b, _c, _d;
|
|
164
|
+
if (this.renderer) {
|
|
165
|
+
return this.arButton;
|
|
166
|
+
}
|
|
167
|
+
if (!window.isSecureContext) {
|
|
168
|
+
throw new Error("WebXR requires a secure context (HTTPS).");
|
|
169
|
+
}
|
|
170
|
+
const canvas = (_a = this.options.canvas) != null ? _a : document.createElement("canvas");
|
|
171
|
+
const renderer = new THREE.WebGLRenderer({
|
|
172
|
+
canvas,
|
|
173
|
+
antialias: true,
|
|
174
|
+
alpha: true
|
|
175
|
+
});
|
|
176
|
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
177
|
+
renderer.setPixelRatio(window.devicePixelRatio);
|
|
178
|
+
renderer.xr.enabled = true;
|
|
179
|
+
renderer.xr.addEventListener("sessionstart", () => {
|
|
180
|
+
var _a2, _b2;
|
|
181
|
+
this.isSessionActive = true;
|
|
182
|
+
(_b2 = (_a2 = this.options).onSessionStart) == null ? void 0 : _b2.call(_a2);
|
|
183
|
+
});
|
|
184
|
+
renderer.xr.addEventListener("sessionend", () => {
|
|
185
|
+
var _a2, _b2;
|
|
186
|
+
this.isSessionActive = false;
|
|
187
|
+
(_b2 = (_a2 = this.options).onSessionEnd) == null ? void 0 : _b2.call(_a2);
|
|
188
|
+
});
|
|
189
|
+
const camera = new THREE.PerspectiveCamera(
|
|
190
|
+
45,
|
|
191
|
+
window.innerWidth / window.innerHeight,
|
|
192
|
+
0.2,
|
|
193
|
+
1e4
|
|
194
|
+
);
|
|
195
|
+
const scene = new THREE.Scene();
|
|
196
|
+
const animationLoop = () => {
|
|
197
|
+
renderer.render(scene, camera);
|
|
198
|
+
};
|
|
199
|
+
renderer.setAnimationLoop(animationLoop);
|
|
200
|
+
const resizeHandler = () => {
|
|
201
|
+
camera.aspect = window.innerWidth / window.innerHeight;
|
|
202
|
+
camera.updateProjectionMatrix();
|
|
203
|
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
204
|
+
};
|
|
205
|
+
window.addEventListener("resize", resizeHandler);
|
|
206
|
+
const overlayRoot = (_b = this.options.overlayRoot) != null ? _b : document.body;
|
|
207
|
+
const arButton = ARButton.createButton(renderer, {
|
|
208
|
+
requiredFeatures: ["camera-access", "dom-overlay"],
|
|
209
|
+
domOverlay: { root: overlayRoot }
|
|
210
|
+
});
|
|
211
|
+
const buttonParent = buttonContainer && buttonContainer instanceof HTMLElement ? buttonContainer : this.options.buttonContainer && this.options.buttonContainer instanceof HTMLElement ? this.options.buttonContainer : overlayRoot;
|
|
212
|
+
if (!buttonParent.contains(arButton)) {
|
|
213
|
+
buttonParent.appendChild(arButton);
|
|
214
|
+
}
|
|
215
|
+
this.renderer = renderer;
|
|
216
|
+
this.camera = camera;
|
|
217
|
+
this.scene = scene;
|
|
218
|
+
this.animationLoop = animationLoop;
|
|
219
|
+
this.arButton = arButton;
|
|
220
|
+
this.resizeHandler = resizeHandler;
|
|
221
|
+
(_d = (_c = this.options).onARButtonCreated) == null ? void 0 : _d.call(_c, arButton);
|
|
222
|
+
return arButton;
|
|
223
|
+
}
|
|
224
|
+
getScene() {
|
|
225
|
+
if (!this.scene) {
|
|
226
|
+
throw new Error("Scene: WebXR controller has not been initialized.");
|
|
227
|
+
}
|
|
228
|
+
return this.scene;
|
|
229
|
+
}
|
|
230
|
+
getCamera() {
|
|
231
|
+
if (!this.camera) {
|
|
232
|
+
throw new Error("Camera: WebXR controller has not been initialized.");
|
|
233
|
+
}
|
|
234
|
+
return this.camera;
|
|
235
|
+
}
|
|
236
|
+
getRenderer() {
|
|
237
|
+
if (!this.renderer) {
|
|
238
|
+
throw new Error("Renderer: WebXR controller has not been initialized.");
|
|
239
|
+
}
|
|
240
|
+
return this.renderer;
|
|
241
|
+
}
|
|
242
|
+
hasActiveSession() {
|
|
243
|
+
var _a;
|
|
244
|
+
return this.isSessionActive && ((_a = this.renderer) == null ? void 0 : _a.xr.isPresenting) === true;
|
|
245
|
+
}
|
|
246
|
+
async captureFrame() {
|
|
247
|
+
var _a, _b;
|
|
248
|
+
const renderer = this.renderer;
|
|
249
|
+
const camera = this.camera;
|
|
250
|
+
if (!renderer || !camera) {
|
|
251
|
+
throw new Error("WebXR: WebXR controller has not been initialized.");
|
|
252
|
+
}
|
|
253
|
+
const session = (_b = (_a = renderer.xr).getSession) == null ? void 0 : _b.call(_a);
|
|
254
|
+
if (!session) {
|
|
255
|
+
throw new Error("WebXR Session: No active WebXR session. Start AR before capturing.");
|
|
256
|
+
}
|
|
257
|
+
const referenceSpace = renderer.xr.getReferenceSpace();
|
|
258
|
+
if (!referenceSpace) {
|
|
259
|
+
throw new Error("WebXR Reference Space: Unable to acquire XR reference space.");
|
|
260
|
+
}
|
|
261
|
+
const gl = renderer.getContext();
|
|
262
|
+
return new Promise((resolve, reject) => {
|
|
263
|
+
session.requestAnimationFrame(async (_time, xrFrame) => {
|
|
264
|
+
var _a2, _b2, _c, _d;
|
|
265
|
+
try {
|
|
266
|
+
const viewerPose = xrFrame.getViewerPose(referenceSpace);
|
|
267
|
+
if (!viewerPose) {
|
|
268
|
+
resolve(null);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
for (const view of viewerPose.views) {
|
|
272
|
+
const xrCamera = view.camera;
|
|
273
|
+
if (!xrCamera) continue;
|
|
274
|
+
const gl2 = gl;
|
|
275
|
+
const bindingCtor = XRWebGLBinding;
|
|
276
|
+
const binding = new bindingCtor(session, gl2);
|
|
277
|
+
const cameraTexture = (_b2 = (_a2 = binding.getCameraImage) == null ? void 0 : _a2.call(binding, xrCamera)) != null ? _b2 : null;
|
|
278
|
+
if (!cameraTexture) continue;
|
|
279
|
+
const width = xrCamera.width;
|
|
280
|
+
const height = xrCamera.height;
|
|
281
|
+
if (!width || !height) continue;
|
|
282
|
+
const frameData = await getCameraTextureAsImage(
|
|
283
|
+
renderer,
|
|
284
|
+
cameraTexture,
|
|
285
|
+
width,
|
|
286
|
+
height
|
|
287
|
+
);
|
|
288
|
+
const intrinsics = getCameraIntrinsics(view.projectionMatrix, {
|
|
289
|
+
width,
|
|
290
|
+
height,
|
|
291
|
+
x: 0,
|
|
292
|
+
y: 0
|
|
293
|
+
});
|
|
294
|
+
if (frameData && intrinsics) {
|
|
295
|
+
const result = await this.options.client.localizeWithFrame(
|
|
296
|
+
frameData,
|
|
297
|
+
intrinsics
|
|
298
|
+
);
|
|
299
|
+
resolve(result);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
resolve(null);
|
|
304
|
+
} catch (error) {
|
|
305
|
+
reject(error);
|
|
306
|
+
} finally {
|
|
307
|
+
gl.bindFramebuffer(
|
|
308
|
+
gl.FRAMEBUFFER,
|
|
309
|
+
(_d = (_c = session.renderState.baseLayer) == null ? void 0 : _c.framebuffer) != null ? _d : null
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
dispose() {
|
|
316
|
+
var _a;
|
|
317
|
+
if (this.resizeHandler) {
|
|
318
|
+
window.removeEventListener("resize", this.resizeHandler);
|
|
319
|
+
}
|
|
320
|
+
if (this.renderer) {
|
|
321
|
+
this.renderer.dispose();
|
|
322
|
+
this.renderer = null;
|
|
323
|
+
}
|
|
324
|
+
this.animationLoop = null;
|
|
325
|
+
this.camera = null;
|
|
326
|
+
this.scene = null;
|
|
327
|
+
if ((_a = this.arButton) == null ? void 0 : _a.parentElement) {
|
|
328
|
+
this.arButton.parentElement.removeChild(this.arButton);
|
|
329
|
+
}
|
|
330
|
+
this.arButton = null;
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
function getCameraIntrinsics(projectionMatrix, viewport) {
|
|
334
|
+
const p = projectionMatrix;
|
|
335
|
+
const u0 = (1 - p[8]) * viewport.width / 2 + viewport.x;
|
|
336
|
+
const v0 = (1 - p[9]) * viewport.height / 2 + viewport.y;
|
|
337
|
+
const ax = viewport.width / 2 * p[0];
|
|
338
|
+
const ay = viewport.height / 2 * p[5];
|
|
339
|
+
return {
|
|
340
|
+
fx: ax,
|
|
341
|
+
fy: ay,
|
|
342
|
+
px: u0,
|
|
343
|
+
py: v0,
|
|
344
|
+
width: viewport.width,
|
|
345
|
+
height: viewport.height
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
async function compressToJpeg(buffer, width, height, quality = 0.8) {
|
|
349
|
+
const canvas = document.createElement("canvas");
|
|
350
|
+
const ctx = canvas.getContext("2d");
|
|
351
|
+
canvas.width = width;
|
|
352
|
+
canvas.height = height;
|
|
353
|
+
const imageData = new ImageData(new Uint8ClampedArray(buffer), width, height);
|
|
354
|
+
ctx == null ? void 0 : ctx.putImageData(imageData, 0, 0);
|
|
355
|
+
return new Promise((resolve) => {
|
|
356
|
+
canvas.toBlob((blob) => resolve(blob != null ? blob : new Blob()), "image/jpeg", quality);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
async function getCameraTextureAsImage(renderer, webGLTexture, width, height) {
|
|
360
|
+
const gl = renderer.getContext();
|
|
361
|
+
if (!gl) return null;
|
|
362
|
+
const framebuffer = gl.createFramebuffer();
|
|
363
|
+
if (!framebuffer) return null;
|
|
364
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
365
|
+
gl.framebufferTexture2D(
|
|
366
|
+
gl.FRAMEBUFFER,
|
|
367
|
+
gl.COLOR_ATTACHMENT0,
|
|
368
|
+
gl.TEXTURE_2D,
|
|
369
|
+
webGLTexture,
|
|
370
|
+
0
|
|
371
|
+
);
|
|
372
|
+
const pixelBuffer = new Uint8Array(width * height * 4);
|
|
373
|
+
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixelBuffer);
|
|
374
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
375
|
+
gl.deleteFramebuffer(framebuffer);
|
|
376
|
+
const flippedData = new Uint8ClampedArray(pixelBuffer.length);
|
|
377
|
+
for (let row = 0; row < height; row += 1) {
|
|
378
|
+
const sourceStart = row * width * 4;
|
|
379
|
+
const destStart = (height - row - 1) * width * 4;
|
|
380
|
+
flippedData.set(
|
|
381
|
+
pixelBuffer.subarray(sourceStart, sourceStart + width * 4),
|
|
382
|
+
destStart
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
const blob = await compressToJpeg(flippedData.buffer, width, height, 0.7);
|
|
386
|
+
if (!blob.size) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
blob,
|
|
391
|
+
width,
|
|
392
|
+
height
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export { DEFAULT_ENDPOINTS, MultisetClient, WebxrController };
|
|
397
|
+
//# sourceMappingURL=index.js.map
|
|
398
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/core/index.ts","../src/lib/webxr/index.ts"],"names":["_a","_b"],"mappings":"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","file":"index.js","sourcesContent":["import axios, { AxiosError } from 'axios';\nimport type {\n ILocalizeAndMapDetails,\n ILocalizeResponse,\n MapType,\n IGetMapsDetailsResponse,\n IMapSetMapsResponse,\n} from './types';\n\nexport interface IMultisetSdkConfig {\n clientId: string;\n clientSecret: string;\n code: string;\n mapType: MapType;\n endpoints?: Partial<IMultisetSdkEndpoints>;\n onAuthorize?: (token: string) => void;\n onFrameCaptured?: (payload: IFrameCaptureEvent) => void;\n onCameraIntrinsics?: (intrinsics: ICameraIntrinsicsEvent) => void;\n onPoseResult?: (payload: IPoseResultEvent) => void;\n onError?: (error: unknown) => void;\n}\n\nexport interface IMultisetSdkEndpoints {\n authUrl: string;\n queryUrl: string;\n mapDetailsUrl: string;\n fileDownloadUrl: string;\n mapSetDetailsUrl: string;\n}\n\nexport interface IFrameCaptureEvent {\n blob: Blob;\n width: number;\n height: number;\n}\n\nexport interface ICameraIntrinsicsEvent {\n fx: number;\n fy: number;\n px: number;\n py: number;\n width: number;\n height: number;\n}\n\nexport interface IPoseResultEvent {\n poseFound: boolean;\n position: {\n x: number;\n y: number;\n z: number;\n };\n rotation: {\n x: number;\n y: number;\n z: number;\n w: number;\n };\n mapIds: string[];\n confidence?: number;\n}\n\nexport interface ILocalizeResultEvent {\n frame: IFrameCaptureEvent;\n intrinsics: ICameraIntrinsicsEvent;\n response: ILocalizeAndMapDetails | null;\n}\n\nexport const DEFAULT_ENDPOINTS: IMultisetSdkEndpoints = {\n authUrl: 'https://api.multiset.ai/v1/m2m/token',\n queryUrl: 'https://api.multiset.ai/v1/vps/map/query-form',\n mapDetailsUrl: 'https://api.multiset.ai/v1/vps/map/',\n mapSetDetailsUrl: 'https://api.multiset.ai/v1/vps/map-set/',\n fileDownloadUrl: 'https://api.multiset.ai/v1/file',\n};\n\n/**\n * Placeholder class to be implemented by porting logic from multiset-webxr-sdk.\n */\nexport class MultisetClient {\n private readonly endpoints: IMultisetSdkEndpoints;\n private accessToken: string | null = null;\n\n constructor(private readonly config: IMultisetSdkConfig) {\n this.config = config;\n this.endpoints = {\n ...DEFAULT_ENDPOINTS,\n ...config.endpoints,\n };\n }\n\n get token(): string | null {\n return this.accessToken;\n }\n\n async authorize(): Promise<string> {\n try {\n const response = await axios.post(\n this.endpoints.authUrl,\n {},\n {\n auth: {\n username: this.config.clientId,\n password: this.config.clientSecret,\n },\n }\n );\n\n const token: string | undefined =\n response.data?.token ?? response.data?.access_token;\n\n if (!token) {\n throw new Error('Authorization succeeded but no token was returned.');\n }\n\n this.accessToken = token;\n this.config.onAuthorize?.(token);\n return token;\n } catch (error) {\n this.handleError(error);\n throw error;\n }\n }\n\n private handleError(error: unknown): void {\n if (axios.isAxiosError(error)) {\n const axiosError = error as AxiosError;\n this.config.onError?.(axiosError);\n } else {\n this.config.onError?.(error);\n }\n }\n\n async localizeWithFrame(\n frame: IFrameCaptureEvent,\n intrinsics: ICameraIntrinsicsEvent\n ): Promise<ILocalizeAndMapDetails | null> {\n if (!this.accessToken) {\n throw new Error('Access token is missing. Call authorize() first.');\n }\n\n this.config.onFrameCaptured?.(frame);\n this.config.onCameraIntrinsics?.(intrinsics);\n\n const queryResult = await this.queryLocalization(frame, intrinsics);\n\n if (queryResult?.localizeData?.poseFound) {\n this.config.onPoseResult?.(queryResult.localizeData);\n }\n\n return queryResult;\n }\n\n private async queryLocalization(\n frame: IFrameCaptureEvent,\n intrinsics: ICameraIntrinsicsEvent\n ): Promise<ILocalizeAndMapDetails | null> {\n const formData = new FormData();\n formData.append('isRightHanded', 'true');\n formData.append('width', `${frame.width}`);\n formData.append('height', `${frame.height}`);\n formData.append('px', `${intrinsics.px}`);\n formData.append('py', `${intrinsics.py}`);\n formData.append('fx', `${intrinsics.fx}`);\n formData.append('fy', `${intrinsics.fy}`);\n formData.append('queryImage', frame.blob);\n\n if (this.config.mapType === 'map') {\n formData.append('mapCode', this.config.code);\n } else {\n formData.append('mapSetCode', this.config.code);\n }\n\n try {\n const response = await axios.post<ILocalizeResponse>(\n this.endpoints.queryUrl,\n formData,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n\n const data = response.data;\n if (!data.poseFound) {\n return null;\n }\n\n const result: ILocalizeAndMapDetails = {\n localizeData: data,\n };\n\n if (data.mapIds?.length) {\n const mapDetails = await this.fetchMapDetails(data.mapIds[0]);\n if (mapDetails) {\n result.mapDetails = mapDetails;\n }\n }\n\n return result;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n\n private async fetchMapDetails(mapId: string): Promise<IGetMapsDetailsResponse | null> {\n try {\n const response = await axios.get<IGetMapsDetailsResponse>(\n `${this.endpoints.mapDetailsUrl}${mapId}`,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n return response.data;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n\n private async fetchMapSetDetails(mapSetId: string): Promise<IMapSetMapsResponse | null> {\n try {\n const response = await axios.get<IMapSetMapsResponse>(\n `${this.endpoints.mapSetDetailsUrl}${mapSetId}`,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n return response.data;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n}\n\nexport type { IMultisetSdkConfig as IMultisetClientOptions };\nexport type {\n MapType,\n ILocalizeResponse,\n ILocalizeAndMapDetails,\n IGetMapsDetailsResponse,\n IMapSetMapsResponse,\n} from './types';\n\n","import * as THREE from 'three';\nimport { ARButton } from 'three/examples/jsm/webxr/ARButton.js';\n\nimport type {\n MultisetClient,\n ICameraIntrinsicsEvent,\n IFrameCaptureEvent,\n ILocalizeAndMapDetails,\n} from '../core';\n\nexport interface IWebxrControllerOptions {\n client: MultisetClient;\n canvas?: HTMLCanvasElement;\n overlayRoot?: HTMLElement;\n buttonContainer?: HTMLElement;\n onARButtonCreated?: (button: HTMLButtonElement) => void;\n onSessionStart?: () => void;\n onSessionEnd?: () => void;\n}\n\ntype XRViewWithCamera = XRView & {\n camera?: {\n width: number;\n height: number;\n [key: string]: unknown;\n };\n};\n\ntype XRWebGLBindingLike = {\n getCameraImage: (camera: XRViewWithCamera['camera']) => WebGLTexture | null;\n};\n\nexport class WebxrController {\n private renderer: THREE.WebGLRenderer | null = null;\n private camera: THREE.PerspectiveCamera | null = null;\n private scene: THREE.Scene | null = null;\n private animationLoop: (() => void) | null = null;\n private arButton: HTMLButtonElement | null = null;\n private resizeHandler: (() => void) | null = null;\n private isSessionActive: boolean = false;\n\n constructor(private readonly options: IWebxrControllerOptions) { }\n\n async initialize(buttonContainer?: HTMLElement): Promise<HTMLButtonElement> {\n if (this.renderer) {\n return this.arButton!;\n }\n\n if (!window.isSecureContext) {\n throw new Error('WebXR requires a secure context (HTTPS).');\n }\n\n const canvas = this.options.canvas ?? document.createElement('canvas');\n\n const renderer = new THREE.WebGLRenderer({\n canvas,\n antialias: true,\n alpha: true,\n });\n renderer.setSize(window.innerWidth, window.innerHeight);\n renderer.setPixelRatio(window.devicePixelRatio);\n renderer.xr.enabled = true;\n\n // Listen for session start/end events\n renderer.xr.addEventListener('sessionstart', () => {\n this.isSessionActive = true;\n this.options.onSessionStart?.();\n });\n\n renderer.xr.addEventListener('sessionend', () => {\n this.isSessionActive = false;\n this.options.onSessionEnd?.();\n });\n\n const camera = new THREE.PerspectiveCamera(\n 45,\n window.innerWidth / window.innerHeight,\n 0.2,\n 10000\n );\n\n const scene = new THREE.Scene();\n\n const animationLoop = () => {\n renderer.render(scene, camera);\n };\n renderer.setAnimationLoop(animationLoop);\n\n const resizeHandler = () => {\n camera.aspect = window.innerWidth / window.innerHeight;\n camera.updateProjectionMatrix();\n renderer.setSize(window.innerWidth, window.innerHeight);\n };\n window.addEventListener('resize', resizeHandler);\n\n const overlayRoot = this.options.overlayRoot ?? document.body;\n const arButton = ARButton.createButton(renderer, {\n requiredFeatures: ['camera-access', 'dom-overlay'],\n domOverlay: { root: overlayRoot },\n }) as HTMLButtonElement;\n\n // Get container at initialization time (like App.tsx does with getElementById)\n // This ensures the container exists in the DOM when we try to append\n // Priority: passed parameter > options.buttonContainer > overlayRoot\n const buttonParent = (buttonContainer && buttonContainer instanceof HTMLElement)\n ? buttonContainer\n : (this.options.buttonContainer && this.options.buttonContainer instanceof HTMLElement)\n ? this.options.buttonContainer\n : overlayRoot;\n\n if (!buttonParent.contains(arButton)) {\n buttonParent.appendChild(arButton);\n }\n\n this.renderer = renderer;\n this.camera = camera;\n this.scene = scene;\n this.animationLoop = animationLoop;\n this.arButton = arButton;\n this.resizeHandler = resizeHandler;\n\n this.options.onARButtonCreated?.(arButton);\n return arButton;\n }\n\n getScene(): THREE.Scene {\n if (!this.scene) {\n throw new Error('Scene: WebXR controller has not been initialized.');\n }\n return this.scene;\n }\n\n getCamera(): THREE.PerspectiveCamera {\n if (!this.camera) {\n throw new Error('Camera: WebXR controller has not been initialized.');\n }\n return this.camera;\n }\n\n getRenderer(): THREE.WebGLRenderer {\n if (!this.renderer) {\n throw new Error('Renderer: WebXR controller has not been initialized.');\n }\n return this.renderer;\n }\n\n hasActiveSession(): boolean {\n return this.isSessionActive && this.renderer?.xr.isPresenting === true;\n }\n\n async captureFrame(): Promise<ILocalizeAndMapDetails | null> {\n const renderer = this.renderer;\n const camera = this.camera;\n if (!renderer || !camera) {\n throw new Error('WebXR: WebXR controller has not been initialized.');\n }\n\n const session = renderer.xr.getSession?.();\n if (!session) {\n throw new Error('WebXR Session: No active WebXR session. Start AR before capturing.');\n }\n\n const referenceSpace = renderer.xr.getReferenceSpace();\n if (!referenceSpace) {\n throw new Error('WebXR Reference Space: Unable to acquire XR reference space.');\n }\n\n const gl = renderer.getContext();\n\n return new Promise((resolve, reject) => {\n session.requestAnimationFrame(async (_time: number, xrFrame: XRFrame) => {\n try {\n const viewerPose = xrFrame.getViewerPose(referenceSpace);\n if (!viewerPose) {\n resolve(null);\n return;\n }\n\n for (const view of viewerPose.views as XRViewWithCamera[]) {\n const xrCamera = view.camera;\n if (!xrCamera) continue;\n\n const gl2 = gl as unknown as WebGL2RenderingContext;\n const bindingCtor = XRWebGLBinding as unknown as {\n new(session: XRSession, context: WebGL2RenderingContext): XRWebGLBindingLike;\n };\n const binding = new bindingCtor(session, gl2);\n const cameraTexture = binding.getCameraImage?.(xrCamera) ?? null;\n if (!cameraTexture) continue;\n\n const width = xrCamera.width;\n const height = xrCamera.height;\n if (!width || !height) continue;\n\n const frameData = await getCameraTextureAsImage(\n renderer,\n cameraTexture,\n width,\n height\n );\n\n const intrinsics = getCameraIntrinsics(view.projectionMatrix, {\n width,\n height,\n x: 0,\n y: 0,\n });\n\n if (frameData && intrinsics) {\n const result = await this.options.client.localizeWithFrame(\n frameData,\n intrinsics\n );\n\n resolve(result);\n return;\n }\n }\n\n resolve(null);\n } catch (error) {\n reject(error);\n } finally {\n gl.bindFramebuffer(\n gl.FRAMEBUFFER,\n session.renderState.baseLayer?.framebuffer ?? null\n );\n }\n });\n });\n }\n\n dispose(): void {\n if (this.resizeHandler) {\n window.removeEventListener('resize', this.resizeHandler);\n }\n\n if (this.renderer) {\n this.renderer.dispose();\n this.renderer = null;\n }\n\n this.animationLoop = null;\n this.camera = null;\n this.scene = null;\n\n if (this.arButton?.parentElement) {\n this.arButton.parentElement.removeChild(this.arButton);\n }\n this.arButton = null;\n }\n}\n\nfunction getCameraIntrinsics(\n projectionMatrix: Float32Array,\n viewport: XRViewport\n): ICameraIntrinsicsEvent {\n const p = projectionMatrix;\n const u0 = ((1 - p[8]) * viewport.width) / 2 + viewport.x;\n const v0 = ((1 - p[9]) * viewport.height) / 2 + viewport.y;\n const ax = (viewport.width / 2) * p[0];\n const ay = (viewport.height / 2) * p[5];\n\n return {\n fx: ax,\n fy: ay,\n px: u0,\n py: v0,\n width: viewport.width,\n height: viewport.height,\n };\n}\n\nasync function compressToJpeg(\n buffer: ArrayBuffer,\n width: number,\n height: number,\n quality = 0.8\n): Promise<Blob> {\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n canvas.width = width;\n canvas.height = height;\n\n const imageData = new ImageData(new Uint8ClampedArray(buffer), width, height);\n ctx?.putImageData(imageData, 0, 0);\n\n return new Promise((resolve) => {\n canvas.toBlob((blob) => resolve(blob ?? new Blob()), 'image/jpeg', quality);\n });\n}\n\nasync function getCameraTextureAsImage(\n renderer: THREE.WebGLRenderer,\n webGLTexture: WebGLTexture,\n width: number,\n height: number\n): Promise<IFrameCaptureEvent | null> {\n const gl = renderer.getContext();\n if (!gl) return null;\n\n const framebuffer = gl.createFramebuffer();\n if (!framebuffer) return null;\n\n gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);\n gl.framebufferTexture2D(\n gl.FRAMEBUFFER,\n gl.COLOR_ATTACHMENT0,\n gl.TEXTURE_2D,\n webGLTexture,\n 0\n );\n\n const pixelBuffer = new Uint8Array(width * height * 4);\n gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixelBuffer);\n\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n gl.deleteFramebuffer(framebuffer);\n\n const flippedData = new Uint8ClampedArray(pixelBuffer.length);\n for (let row = 0; row < height; row += 1) {\n const sourceStart = row * width * 4;\n const destStart = (height - row - 1) * width * 4;\n flippedData.set(\n pixelBuffer.subarray(sourceStart, sourceStart + width * 4),\n destStart\n );\n }\n\n const blob = await compressToJpeg(flippedData.buffer, width, height, 0.7);\n\n if (!blob.size) {\n return null;\n }\n\n return {\n blob,\n width,\n height,\n };\n}\n\n"]}
|