@galihru/orbinexsim 0.1.1 → 0.1.3
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/README.md +344 -17
- package/dist/ar-runtime.cjs +526 -0
- package/dist/ar-runtime.d.cts +100 -0
- package/dist/ar-runtime.d.ts +100 -0
- package/dist/ar-runtime.js +28 -0
- package/dist/chunk-JF7PGQOV.js +491 -0
- package/dist/index.cjs +514 -11
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +29 -2
- package/package.json +57 -52
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/ar-runtime.ts
|
|
21
|
+
var ar_runtime_exports = {};
|
|
22
|
+
__export(ar_runtime_exports, {
|
|
23
|
+
bindMarkerTracking: () => bindMarkerTracking,
|
|
24
|
+
createDefaultArMarkers: () => createDefaultArMarkers,
|
|
25
|
+
createMarkersFromCatalog: () => createMarkersFromCatalog,
|
|
26
|
+
createPrimitiveModelFromCatalogEntry: () => createPrimitiveModelFromCatalogEntry,
|
|
27
|
+
ensureArMarkers: () => ensureArMarkers,
|
|
28
|
+
extractCatalogEntriesFromPayload: () => extractCatalogEntriesFromPayload,
|
|
29
|
+
loadCatalogFromProxy: () => loadCatalogFromProxy,
|
|
30
|
+
parseArRequestFromSearch: () => parseArRequestFromSearch,
|
|
31
|
+
requestRuntimePermissions: () => requestRuntimePermissions,
|
|
32
|
+
resolveArMarkerHint: () => resolveArMarkerHint,
|
|
33
|
+
resolveCatalogProxyUrl: () => resolveCatalogProxyUrl,
|
|
34
|
+
resolveObjectNameForMarker: () => resolveObjectNameForMarker
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(ar_runtime_exports);
|
|
37
|
+
var DEFAULT_CATALOG_PROXY_PARAM_KEYS = ["catalogProxy", "catalog", "proxy", "db", "catalogUrl"];
|
|
38
|
+
var MARKER_HIRO_URL = "https://raw.githubusercontent.com/AR-js-org/AR.js/master/data/images/hiro.png";
|
|
39
|
+
var MARKER_KANJI_URL = "https://raw.githubusercontent.com/AR-js-org/AR.js/master/data/images/kanji.png";
|
|
40
|
+
var DEFAULT_PERMISSION_SUMMARY = {
|
|
41
|
+
camera: "unsupported",
|
|
42
|
+
microphone: "unsupported",
|
|
43
|
+
geolocation: "unsupported",
|
|
44
|
+
motionSensors: "unsupported"
|
|
45
|
+
};
|
|
46
|
+
function normalizePermissionResult(value) {
|
|
47
|
+
return value === "granted" || value === "denied" || value === "unsupported" ? value : "unsupported";
|
|
48
|
+
}
|
|
49
|
+
function clamp(value, min, max) {
|
|
50
|
+
return Math.min(max, Math.max(min, value));
|
|
51
|
+
}
|
|
52
|
+
function normalizeCssColor(value, fallback) {
|
|
53
|
+
const candidate = (value || "").trim();
|
|
54
|
+
if (!candidate) {
|
|
55
|
+
return fallback;
|
|
56
|
+
}
|
|
57
|
+
if (typeof document === "undefined") {
|
|
58
|
+
return candidate;
|
|
59
|
+
}
|
|
60
|
+
const probe = document.createElement("span");
|
|
61
|
+
probe.style.color = "";
|
|
62
|
+
probe.style.color = candidate;
|
|
63
|
+
return probe.style.color ? candidate : fallback;
|
|
64
|
+
}
|
|
65
|
+
function isBlackLikeColor(value) {
|
|
66
|
+
const normalized = value.replace(/\s+/g, "").toLowerCase();
|
|
67
|
+
return normalized === "#000" || normalized === "#000000" || normalized === "black" || normalized === "rgb(0,0,0)" || normalized === "rgba(0,0,0,1)";
|
|
68
|
+
}
|
|
69
|
+
function asRecord(value) {
|
|
70
|
+
if (!value || typeof value !== "object") {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
function readFirstString(source, keys) {
|
|
76
|
+
for (const key of keys) {
|
|
77
|
+
const raw = source[key];
|
|
78
|
+
if (typeof raw === "string" && raw.trim()) {
|
|
79
|
+
return raw.trim();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return "";
|
|
83
|
+
}
|
|
84
|
+
function readFirstNumber(source, keys) {
|
|
85
|
+
for (const key of keys) {
|
|
86
|
+
const raw = source[key];
|
|
87
|
+
const next = typeof raw === "number" ? raw : typeof raw === "string" ? Number(raw) : NaN;
|
|
88
|
+
if (Number.isFinite(next)) {
|
|
89
|
+
return next;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
function toOptionalNumber(value) {
|
|
95
|
+
return value === null ? void 0 : value;
|
|
96
|
+
}
|
|
97
|
+
function toOptionalNullableNumber(value) {
|
|
98
|
+
if (value === null) {
|
|
99
|
+
return void 0;
|
|
100
|
+
}
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
function toOptionalText(value) {
|
|
104
|
+
return value ? value : void 0;
|
|
105
|
+
}
|
|
106
|
+
async function requestCameraPermission() {
|
|
107
|
+
if (!window.isSecureContext || !navigator.mediaDevices || typeof navigator.mediaDevices.getUserMedia !== "function") {
|
|
108
|
+
return "unsupported";
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
112
|
+
video: { facingMode: "environment" },
|
|
113
|
+
audio: false
|
|
114
|
+
});
|
|
115
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
116
|
+
return "granted";
|
|
117
|
+
} catch {
|
|
118
|
+
return "denied";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async function requestMicrophonePermission() {
|
|
122
|
+
if (!window.isSecureContext || !navigator.mediaDevices || typeof navigator.mediaDevices.getUserMedia !== "function") {
|
|
123
|
+
return "unsupported";
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
|
|
127
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
128
|
+
return "granted";
|
|
129
|
+
} catch {
|
|
130
|
+
return "denied";
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async function requestGeolocationPermission() {
|
|
134
|
+
if (!window.isSecureContext || !("geolocation" in navigator) || !navigator.geolocation) {
|
|
135
|
+
return "unsupported";
|
|
136
|
+
}
|
|
137
|
+
return new Promise((resolve) => {
|
|
138
|
+
navigator.geolocation.getCurrentPosition(
|
|
139
|
+
() => resolve("granted"),
|
|
140
|
+
() => resolve("denied"),
|
|
141
|
+
{
|
|
142
|
+
enableHighAccuracy: false,
|
|
143
|
+
timeout: 7e3,
|
|
144
|
+
maximumAge: 0
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
async function requestMotionSensorPermission() {
|
|
150
|
+
const motionApi = window.DeviceMotionEvent;
|
|
151
|
+
if (!motionApi || typeof motionApi.requestPermission !== "function") {
|
|
152
|
+
return "unsupported";
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
const result = await motionApi.requestPermission();
|
|
156
|
+
return result === "granted" ? "granted" : "denied";
|
|
157
|
+
} catch {
|
|
158
|
+
return "denied";
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function resolveHostNode(target) {
|
|
162
|
+
if (typeof target !== "string") {
|
|
163
|
+
if (target instanceof Document) {
|
|
164
|
+
return target.documentElement;
|
|
165
|
+
}
|
|
166
|
+
return target;
|
|
167
|
+
}
|
|
168
|
+
const found = document.querySelector(target);
|
|
169
|
+
if (!found) {
|
|
170
|
+
throw new Error(`Target element not found for selector: ${target}`);
|
|
171
|
+
}
|
|
172
|
+
return found;
|
|
173
|
+
}
|
|
174
|
+
function normalizeMarkerConfig(config) {
|
|
175
|
+
const preset = (config.preset || "hiro").trim() || "hiro";
|
|
176
|
+
const markerImage = (config.markerImage || (preset === "kanji" ? MARKER_KANJI_URL : MARKER_HIRO_URL)).trim();
|
|
177
|
+
const markerLink = (config.markerLink || markerImage).trim();
|
|
178
|
+
const markerLabel = (config.markerLabel || (preset === "kanji" ? "Kanji" : "Hiro")).trim();
|
|
179
|
+
const id = (config.id || `orbinex-marker-${preset}`).trim();
|
|
180
|
+
return {
|
|
181
|
+
id,
|
|
182
|
+
preset,
|
|
183
|
+
model: (config.model || "Bumi").trim() || "Bumi",
|
|
184
|
+
markerLabel,
|
|
185
|
+
markerImage,
|
|
186
|
+
markerLink,
|
|
187
|
+
smooth: config.smooth ?? true,
|
|
188
|
+
smoothCount: config.smoothCount ?? 8,
|
|
189
|
+
smoothTolerance: config.smoothTolerance ?? 0.01,
|
|
190
|
+
smoothThreshold: config.smoothThreshold ?? 5
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function ensureModelRoot(markerEl) {
|
|
194
|
+
if (markerEl.querySelector("[data-model-root]")) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const modelRoot = document.createElement("a-entity");
|
|
198
|
+
modelRoot.setAttribute("data-model-root", "");
|
|
199
|
+
modelRoot.setAttribute("position", "0 0 0");
|
|
200
|
+
markerEl.appendChild(modelRoot);
|
|
201
|
+
}
|
|
202
|
+
function applyMarkerDataset(markerEl, marker) {
|
|
203
|
+
markerEl.id = marker.id;
|
|
204
|
+
markerEl.dataset.model = marker.model;
|
|
205
|
+
markerEl.dataset.markerLabel = marker.markerLabel;
|
|
206
|
+
markerEl.dataset.markerImage = marker.markerImage;
|
|
207
|
+
markerEl.dataset.markerLink = marker.markerLink;
|
|
208
|
+
markerEl.setAttribute("preset", marker.preset);
|
|
209
|
+
markerEl.setAttribute("emitevents", "true");
|
|
210
|
+
markerEl.setAttribute("smooth", marker.smooth ? "true" : "false");
|
|
211
|
+
markerEl.setAttribute("smoothCount", String(marker.smoothCount));
|
|
212
|
+
markerEl.setAttribute("smoothTolerance", String(marker.smoothTolerance));
|
|
213
|
+
markerEl.setAttribute("smoothThreshold", String(marker.smoothThreshold));
|
|
214
|
+
}
|
|
215
|
+
function resolveObjectNameForMarker(markerNode, fallbackModel = "Bumi") {
|
|
216
|
+
const markerModel = (markerNode?.getAttribute("data-model") || markerNode?.getAttribute("data-object") || "").trim();
|
|
217
|
+
return markerModel || fallbackModel;
|
|
218
|
+
}
|
|
219
|
+
function buildMarkerDetectionSummary(markerNode, visibilityState) {
|
|
220
|
+
const markerId = markerNode.id || markerNode.dataset.markerLabel || markerNode.dataset.model || "marker";
|
|
221
|
+
const markerLabel = (markerNode.dataset.markerLabel || markerId).trim() || markerId;
|
|
222
|
+
const markerModel = resolveObjectNameForMarker(markerNode, "Bumi");
|
|
223
|
+
const visibleCount = Array.from(visibilityState.values()).filter(Boolean).length;
|
|
224
|
+
return {
|
|
225
|
+
markerId,
|
|
226
|
+
markerLabel,
|
|
227
|
+
markerModel,
|
|
228
|
+
markerNode,
|
|
229
|
+
visibleCount,
|
|
230
|
+
isAnyVisible: visibleCount > 0
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function bindMarkerTracking(markerTargets, handlers = {}) {
|
|
234
|
+
const markerNodes = Array.from(markerTargets).filter((node) => node instanceof HTMLElement);
|
|
235
|
+
const visibilityState = /* @__PURE__ */ new Map();
|
|
236
|
+
const disposers = [];
|
|
237
|
+
markerNodes.forEach((markerNode, index) => {
|
|
238
|
+
const markerId = markerNode.id || `marker-${index + 1}`;
|
|
239
|
+
visibilityState.set(markerId, false);
|
|
240
|
+
const onFound = () => {
|
|
241
|
+
visibilityState.set(markerId, true);
|
|
242
|
+
const summary = buildMarkerDetectionSummary(markerNode, visibilityState);
|
|
243
|
+
handlers.onMarkerFound?.(summary);
|
|
244
|
+
handlers.onVisibilityChange?.(summary);
|
|
245
|
+
};
|
|
246
|
+
const onLost = () => {
|
|
247
|
+
visibilityState.set(markerId, false);
|
|
248
|
+
const summary = buildMarkerDetectionSummary(markerNode, visibilityState);
|
|
249
|
+
handlers.onMarkerLost?.(summary);
|
|
250
|
+
handlers.onVisibilityChange?.(summary);
|
|
251
|
+
};
|
|
252
|
+
markerNode.addEventListener("markerFound", onFound);
|
|
253
|
+
markerNode.addEventListener("markerLost", onLost);
|
|
254
|
+
disposers.push(() => {
|
|
255
|
+
markerNode.removeEventListener("markerFound", onFound);
|
|
256
|
+
markerNode.removeEventListener("markerLost", onLost);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
return () => {
|
|
260
|
+
disposers.forEach((dispose) => dispose());
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function resolveCatalogProxyUrl(search = window.location.search, paramKeys = DEFAULT_CATALOG_PROXY_PARAM_KEYS) {
|
|
264
|
+
const params = typeof search === "string" ? new URLSearchParams(search) : search;
|
|
265
|
+
for (const key of paramKeys) {
|
|
266
|
+
const value = (params.get(key) || "").trim();
|
|
267
|
+
if (!value) {
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
return new URL(value, window.location.href).toString();
|
|
272
|
+
} catch {
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return "";
|
|
276
|
+
}
|
|
277
|
+
function normalizeCatalogEntry(value) {
|
|
278
|
+
const source = asRecord(value);
|
|
279
|
+
if (!source) {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
const name = readFirstString(source, ["name", "label", "title", "objectName", "id"]);
|
|
283
|
+
if (!name) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
const kind = readFirstString(source, ["kind", "type", "category", "class"]);
|
|
287
|
+
const markerPreset = readFirstString(source, ["markerPreset", "marker", "preset", "markerType"]);
|
|
288
|
+
return {
|
|
289
|
+
name,
|
|
290
|
+
kind: toOptionalText(kind),
|
|
291
|
+
massKg: toOptionalNumber(readFirstNumber(source, ["massKg", "mass_kg", "mass", "kgMass"])),
|
|
292
|
+
radiusMeters: toOptionalNumber(readFirstNumber(source, ["radiusMeters", "radius_m", "radius", "sizeMeters"])),
|
|
293
|
+
color: toOptionalText(readFirstString(source, ["color", "hexColor", "colour", "markerColor"])),
|
|
294
|
+
parentName: toOptionalText(readFirstString(source, ["parentName", "parent", "orbitParent", "parent_name"])),
|
|
295
|
+
semiMajorMeters: toOptionalNullableNumber(readFirstNumber(source, ["semiMajorMeters", "semi_major_m", "semiMajor", "semiMajorAxis"])),
|
|
296
|
+
periodDays: toOptionalNullableNumber(readFirstNumber(source, ["periodDays", "period_days", "orbitalPeriodDays", "period"])),
|
|
297
|
+
rotationHours: toOptionalNullableNumber(readFirstNumber(source, ["rotationHours", "rotation_hours", "rotationPeriodHours", "rotation"])),
|
|
298
|
+
inclinationDeg: toOptionalNullableNumber(readFirstNumber(source, ["inclinationDeg", "inclination_deg", "inclination"])),
|
|
299
|
+
markerId: toOptionalText(readFirstString(source, ["markerId", "idMarker", "marker_id"])),
|
|
300
|
+
markerPreset: toOptionalText(markerPreset),
|
|
301
|
+
markerLabel: toOptionalText(readFirstString(source, ["markerLabel", "markerName", "labelMarker", "marker_label"])),
|
|
302
|
+
markerImage: toOptionalText(readFirstString(source, ["markerImage", "marker_image", "markerUrl", "image"])),
|
|
303
|
+
markerLink: toOptionalText(readFirstString(source, ["markerLink", "marker_link", "link"])),
|
|
304
|
+
markerModel: toOptionalText(readFirstString(source, ["markerModel", "model", "target", "object"]))
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function pickCatalogArrayCandidate(payload) {
|
|
308
|
+
if (Array.isArray(payload)) {
|
|
309
|
+
return payload;
|
|
310
|
+
}
|
|
311
|
+
const source = asRecord(payload);
|
|
312
|
+
if (!source) {
|
|
313
|
+
return [];
|
|
314
|
+
}
|
|
315
|
+
const preferredKeys = ["entries", "items", "catalog", "objects", "bodies", "data", "results"];
|
|
316
|
+
for (const key of preferredKeys) {
|
|
317
|
+
const value = source[key];
|
|
318
|
+
if (Array.isArray(value)) {
|
|
319
|
+
return value;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
for (const value of Object.values(source)) {
|
|
323
|
+
if (Array.isArray(value)) {
|
|
324
|
+
return value;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return [];
|
|
328
|
+
}
|
|
329
|
+
function extractCatalogEntriesFromPayload(payload) {
|
|
330
|
+
const rawItems = pickCatalogArrayCandidate(payload);
|
|
331
|
+
const normalized = rawItems.map((item) => normalizeCatalogEntry(item)).filter((item) => !!item);
|
|
332
|
+
const dedupedByName = /* @__PURE__ */ new Map();
|
|
333
|
+
normalized.forEach((entry) => {
|
|
334
|
+
dedupedByName.set(entry.name.toLowerCase(), entry);
|
|
335
|
+
});
|
|
336
|
+
return Array.from(dedupedByName.values());
|
|
337
|
+
}
|
|
338
|
+
async function loadCatalogFromProxy(proxyUrl, options = {}) {
|
|
339
|
+
const timeoutMs = options.timeoutMs ?? 12e3;
|
|
340
|
+
const supportsAbort = typeof AbortController !== "undefined";
|
|
341
|
+
const abortController = supportsAbort ? new AbortController() : null;
|
|
342
|
+
const timeoutId = supportsAbort ? window.setTimeout(() => abortController?.abort(), timeoutMs) : 0;
|
|
343
|
+
try {
|
|
344
|
+
const response = await fetch(proxyUrl, {
|
|
345
|
+
...options.requestInit,
|
|
346
|
+
cache: "no-store",
|
|
347
|
+
signal: abortController ? abortController.signal : void 0
|
|
348
|
+
});
|
|
349
|
+
if (!response.ok) {
|
|
350
|
+
return [];
|
|
351
|
+
}
|
|
352
|
+
const payload = await response.json();
|
|
353
|
+
return extractCatalogEntriesFromPayload(payload);
|
|
354
|
+
} catch {
|
|
355
|
+
return [];
|
|
356
|
+
} finally {
|
|
357
|
+
if (supportsAbort) {
|
|
358
|
+
window.clearTimeout(timeoutId);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function createMarkersFromCatalog(entries, fallbackMarkers = []) {
|
|
363
|
+
const markerMap = /* @__PURE__ */ new Map();
|
|
364
|
+
const addMarker = (markerConfig) => {
|
|
365
|
+
const marker = normalizeMarkerConfig(markerConfig);
|
|
366
|
+
markerMap.set(marker.id || marker.preset, marker);
|
|
367
|
+
};
|
|
368
|
+
fallbackMarkers.forEach(addMarker);
|
|
369
|
+
entries.forEach((entry) => {
|
|
370
|
+
const markerPreset = (entry.markerPreset || "").trim();
|
|
371
|
+
const markerId = (entry.markerId || "").trim();
|
|
372
|
+
if (!markerPreset && !markerId) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
addMarker({
|
|
376
|
+
id: markerId || void 0,
|
|
377
|
+
preset: markerPreset || void 0,
|
|
378
|
+
model: entry.markerModel || entry.name,
|
|
379
|
+
markerLabel: entry.markerLabel || entry.name,
|
|
380
|
+
markerImage: entry.markerImage,
|
|
381
|
+
markerLink: entry.markerLink || entry.markerImage
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
return Array.from(markerMap.values());
|
|
385
|
+
}
|
|
386
|
+
function createPrimitiveModelFromCatalogEntry(target, entry, options = {}) {
|
|
387
|
+
const hostNode = resolveHostNode(target);
|
|
388
|
+
const wrapper = document.createElement("a-entity");
|
|
389
|
+
const radiusScale = options.radiusScale ?? 1;
|
|
390
|
+
const radiusMeters = Number.isFinite(entry.radiusMeters) ? Number(entry.radiusMeters) : 6371e3;
|
|
391
|
+
const radius = clamp((0.08 + Math.log10(Math.max(radiusMeters, 1)) * 0.04) * radiusScale, 0.03, 0.68);
|
|
392
|
+
const fallbackColor = (options.defaultColor || "#84aee4").trim() || "#84aee4";
|
|
393
|
+
let color = normalizeCssColor(entry.color, fallbackColor);
|
|
394
|
+
const normalizedName = (entry.name || "").trim().toLowerCase();
|
|
395
|
+
if ((normalizedName === "matahari" || normalizedName === "sun") && isBlackLikeColor(color)) {
|
|
396
|
+
color = "#ffd37c";
|
|
397
|
+
}
|
|
398
|
+
wrapper.setAttribute("position", options.position || "0 0 0");
|
|
399
|
+
wrapper.setAttribute("data-proxy-object", entry.name);
|
|
400
|
+
const bodyEntity = document.createElement("a-entity");
|
|
401
|
+
bodyEntity.setAttribute("geometry", `primitive: sphere; radius: ${radius.toFixed(4)}`);
|
|
402
|
+
bodyEntity.setAttribute("material", `shader: flat; color: ${color}; side: double; depthTest: false; depthWrite: false`);
|
|
403
|
+
wrapper.appendChild(bodyEntity);
|
|
404
|
+
if (options.includeLabel !== false) {
|
|
405
|
+
const labelEntity = document.createElement("a-text");
|
|
406
|
+
labelEntity.setAttribute("value", entry.name);
|
|
407
|
+
labelEntity.setAttribute("align", "center");
|
|
408
|
+
labelEntity.setAttribute("width", "2.5");
|
|
409
|
+
labelEntity.setAttribute("color", "#d7e7ff");
|
|
410
|
+
labelEntity.setAttribute("position", `0 ${(radius * 1.85).toFixed(4)} 0`);
|
|
411
|
+
labelEntity.setAttribute("side", "double");
|
|
412
|
+
wrapper.appendChild(labelEntity);
|
|
413
|
+
}
|
|
414
|
+
hostNode.appendChild(wrapper);
|
|
415
|
+
return wrapper;
|
|
416
|
+
}
|
|
417
|
+
function parseArRequestFromSearch(search = window.location.search, options = {}) {
|
|
418
|
+
const params = typeof search === "string" ? new URLSearchParams(search) : search;
|
|
419
|
+
const modelParamKeys = options.modelParamKeys ?? ["model", "object", "target", "name"];
|
|
420
|
+
const defaultModel = (options.defaultModel || "Bumi").trim() || "Bumi";
|
|
421
|
+
const defaultAltModel = (options.defaultAltModel || "Mars").trim() || "Mars";
|
|
422
|
+
const defaultBuildToken = (options.defaultBuildToken || "latest").trim() || "latest";
|
|
423
|
+
let model = "";
|
|
424
|
+
for (const key of modelParamKeys) {
|
|
425
|
+
const value = (params.get(key) || "").trim();
|
|
426
|
+
if (value) {
|
|
427
|
+
model = value;
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return {
|
|
432
|
+
model: model || defaultModel,
|
|
433
|
+
altModel: (params.get("altModel") || defaultAltModel).trim() || defaultAltModel,
|
|
434
|
+
buildToken: (params.get("build") || defaultBuildToken).trim() || defaultBuildToken
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
function createDefaultArMarkers(model = "Bumi", altModel = "Mars") {
|
|
438
|
+
return [
|
|
439
|
+
{
|
|
440
|
+
id: "orbinex-marker-hiro",
|
|
441
|
+
preset: "hiro",
|
|
442
|
+
model,
|
|
443
|
+
markerLabel: "Hiro",
|
|
444
|
+
markerImage: MARKER_HIRO_URL,
|
|
445
|
+
markerLink: MARKER_HIRO_URL
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
id: "orbinex-marker-kanji",
|
|
449
|
+
preset: "kanji",
|
|
450
|
+
model: altModel,
|
|
451
|
+
markerLabel: "Kanji",
|
|
452
|
+
markerImage: MARKER_KANJI_URL,
|
|
453
|
+
markerLink: MARKER_KANJI_URL
|
|
454
|
+
}
|
|
455
|
+
];
|
|
456
|
+
}
|
|
457
|
+
function ensureArMarkers(sceneTarget, markerConfigs, options = {}) {
|
|
458
|
+
const sceneEl = resolveHostNode(sceneTarget);
|
|
459
|
+
const createMissing = options.createMissing ?? true;
|
|
460
|
+
const ensureModelRootEnabled = options.ensureModelRoot ?? true;
|
|
461
|
+
const ensuredMarkers = [];
|
|
462
|
+
markerConfigs.forEach((config) => {
|
|
463
|
+
const marker = normalizeMarkerConfig(config);
|
|
464
|
+
let markerEl = sceneEl.querySelector(`#${CSS.escape(marker.id)}`);
|
|
465
|
+
if (!markerEl && marker.preset) {
|
|
466
|
+
markerEl = sceneEl.querySelector(`a-marker[preset="${marker.preset}"]`);
|
|
467
|
+
}
|
|
468
|
+
if (!markerEl && createMissing) {
|
|
469
|
+
markerEl = document.createElement("a-marker");
|
|
470
|
+
sceneEl.appendChild(markerEl);
|
|
471
|
+
}
|
|
472
|
+
if (!markerEl) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
markerEl.querySelectorAll("a-plane").forEach((planeNode) => planeNode.remove());
|
|
476
|
+
applyMarkerDataset(markerEl, marker);
|
|
477
|
+
if (ensureModelRootEnabled) {
|
|
478
|
+
ensureModelRoot(markerEl);
|
|
479
|
+
}
|
|
480
|
+
ensuredMarkers.push(markerEl);
|
|
481
|
+
});
|
|
482
|
+
return ensuredMarkers;
|
|
483
|
+
}
|
|
484
|
+
function resolveArMarkerHint(markerNode, fallbackUrl = MARKER_HIRO_URL) {
|
|
485
|
+
const markerImage = (markerNode?.getAttribute("data-marker-image") || fallbackUrl).trim() || fallbackUrl;
|
|
486
|
+
const markerLink = (markerNode?.getAttribute("data-marker-link") || markerImage).trim() || markerImage;
|
|
487
|
+
const markerLabel = (markerNode?.getAttribute("data-marker-label") || "Hiro").trim() || "Hiro";
|
|
488
|
+
return {
|
|
489
|
+
markerImage,
|
|
490
|
+
markerLink,
|
|
491
|
+
markerLabel
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
async function requestRuntimePermissions(options = {}) {
|
|
495
|
+
const wantsCamera = options.camera ?? false;
|
|
496
|
+
const wantsMicrophone = options.microphone ?? false;
|
|
497
|
+
const wantsGeolocation = options.geolocation ?? false;
|
|
498
|
+
const wantsMotionSensors = options.motionSensors ?? false;
|
|
499
|
+
const [camera, microphone, geolocation, motionSensors] = await Promise.all([
|
|
500
|
+
wantsCamera ? requestCameraPermission() : Promise.resolve(DEFAULT_PERMISSION_SUMMARY.camera),
|
|
501
|
+
wantsMicrophone ? requestMicrophonePermission() : Promise.resolve(DEFAULT_PERMISSION_SUMMARY.microphone),
|
|
502
|
+
wantsGeolocation ? requestGeolocationPermission() : Promise.resolve(DEFAULT_PERMISSION_SUMMARY.geolocation),
|
|
503
|
+
wantsMotionSensors ? requestMotionSensorPermission() : Promise.resolve(DEFAULT_PERMISSION_SUMMARY.motionSensors)
|
|
504
|
+
]);
|
|
505
|
+
return {
|
|
506
|
+
camera: normalizePermissionResult(camera),
|
|
507
|
+
microphone: normalizePermissionResult(microphone),
|
|
508
|
+
geolocation: normalizePermissionResult(geolocation),
|
|
509
|
+
motionSensors: normalizePermissionResult(motionSensors)
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
513
|
+
0 && (module.exports = {
|
|
514
|
+
bindMarkerTracking,
|
|
515
|
+
createDefaultArMarkers,
|
|
516
|
+
createMarkersFromCatalog,
|
|
517
|
+
createPrimitiveModelFromCatalogEntry,
|
|
518
|
+
ensureArMarkers,
|
|
519
|
+
extractCatalogEntriesFromPayload,
|
|
520
|
+
loadCatalogFromProxy,
|
|
521
|
+
parseArRequestFromSearch,
|
|
522
|
+
requestRuntimePermissions,
|
|
523
|
+
resolveArMarkerHint,
|
|
524
|
+
resolveCatalogProxyUrl,
|
|
525
|
+
resolveObjectNameForMarker
|
|
526
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
type PermissionResult = "granted" | "denied" | "unsupported";
|
|
2
|
+
interface PermissionRequestOptions {
|
|
3
|
+
camera?: boolean;
|
|
4
|
+
microphone?: boolean;
|
|
5
|
+
geolocation?: boolean;
|
|
6
|
+
motionSensors?: boolean;
|
|
7
|
+
}
|
|
8
|
+
interface PermissionRequestSummary {
|
|
9
|
+
camera: PermissionResult;
|
|
10
|
+
microphone: PermissionResult;
|
|
11
|
+
geolocation: PermissionResult;
|
|
12
|
+
motionSensors: PermissionResult;
|
|
13
|
+
}
|
|
14
|
+
interface ArRequestParseOptions {
|
|
15
|
+
modelParamKeys?: string[];
|
|
16
|
+
defaultModel?: string;
|
|
17
|
+
defaultAltModel?: string;
|
|
18
|
+
defaultBuildToken?: string;
|
|
19
|
+
}
|
|
20
|
+
interface ArRequestSummary {
|
|
21
|
+
model: string;
|
|
22
|
+
altModel: string;
|
|
23
|
+
buildToken: string;
|
|
24
|
+
}
|
|
25
|
+
interface ArMarkerConfig {
|
|
26
|
+
id?: string;
|
|
27
|
+
preset?: string;
|
|
28
|
+
model?: string;
|
|
29
|
+
markerLabel?: string;
|
|
30
|
+
markerImage?: string;
|
|
31
|
+
markerLink?: string;
|
|
32
|
+
smooth?: boolean;
|
|
33
|
+
smoothCount?: number;
|
|
34
|
+
smoothTolerance?: number;
|
|
35
|
+
smoothThreshold?: number;
|
|
36
|
+
}
|
|
37
|
+
interface EnsureArMarkersOptions {
|
|
38
|
+
createMissing?: boolean;
|
|
39
|
+
ensureModelRoot?: boolean;
|
|
40
|
+
}
|
|
41
|
+
interface ArMarkerHintSummary {
|
|
42
|
+
markerImage: string;
|
|
43
|
+
markerLink: string;
|
|
44
|
+
markerLabel: string;
|
|
45
|
+
}
|
|
46
|
+
interface ArMarkerDetectionSummary {
|
|
47
|
+
markerId: string;
|
|
48
|
+
markerLabel: string;
|
|
49
|
+
markerModel: string;
|
|
50
|
+
markerNode: HTMLElement;
|
|
51
|
+
visibleCount: number;
|
|
52
|
+
isAnyVisible: boolean;
|
|
53
|
+
}
|
|
54
|
+
interface MarkerTrackingHandlers {
|
|
55
|
+
onMarkerFound?: (summary: ArMarkerDetectionSummary) => void;
|
|
56
|
+
onMarkerLost?: (summary: ArMarkerDetectionSummary) => void;
|
|
57
|
+
onVisibilityChange?: (summary: ArMarkerDetectionSummary) => void;
|
|
58
|
+
}
|
|
59
|
+
interface CatalogObjectEntry {
|
|
60
|
+
name: string;
|
|
61
|
+
kind?: string;
|
|
62
|
+
massKg?: number;
|
|
63
|
+
radiusMeters?: number;
|
|
64
|
+
color?: string;
|
|
65
|
+
parentName?: string | null;
|
|
66
|
+
semiMajorMeters?: number | null;
|
|
67
|
+
periodDays?: number | null;
|
|
68
|
+
rotationHours?: number | null;
|
|
69
|
+
inclinationDeg?: number | null;
|
|
70
|
+
markerId?: string;
|
|
71
|
+
markerPreset?: string;
|
|
72
|
+
markerLabel?: string;
|
|
73
|
+
markerImage?: string;
|
|
74
|
+
markerLink?: string;
|
|
75
|
+
markerModel?: string;
|
|
76
|
+
}
|
|
77
|
+
interface CatalogProxyLoadOptions {
|
|
78
|
+
requestInit?: RequestInit;
|
|
79
|
+
timeoutMs?: number;
|
|
80
|
+
}
|
|
81
|
+
interface PrimitiveModelCreateOptions {
|
|
82
|
+
includeLabel?: boolean;
|
|
83
|
+
radiusScale?: number;
|
|
84
|
+
defaultColor?: string;
|
|
85
|
+
position?: string;
|
|
86
|
+
}
|
|
87
|
+
declare function resolveObjectNameForMarker(markerNode: Element | null | undefined, fallbackModel?: string): string;
|
|
88
|
+
declare function bindMarkerTracking(markerTargets: Iterable<Element>, handlers?: MarkerTrackingHandlers): () => void;
|
|
89
|
+
declare function resolveCatalogProxyUrl(search?: string | URLSearchParams, paramKeys?: string[]): string;
|
|
90
|
+
declare function extractCatalogEntriesFromPayload(payload: unknown): CatalogObjectEntry[];
|
|
91
|
+
declare function loadCatalogFromProxy(proxyUrl: string, options?: CatalogProxyLoadOptions): Promise<CatalogObjectEntry[]>;
|
|
92
|
+
declare function createMarkersFromCatalog(entries: CatalogObjectEntry[], fallbackMarkers?: ArMarkerConfig[]): ArMarkerConfig[];
|
|
93
|
+
declare function createPrimitiveModelFromCatalogEntry(target: string | Element, entry: CatalogObjectEntry, options?: PrimitiveModelCreateOptions): HTMLElement;
|
|
94
|
+
declare function parseArRequestFromSearch(search?: string | URLSearchParams, options?: ArRequestParseOptions): ArRequestSummary;
|
|
95
|
+
declare function createDefaultArMarkers(model?: string, altModel?: string): ArMarkerConfig[];
|
|
96
|
+
declare function ensureArMarkers(sceneTarget: string | Element, markerConfigs: ArMarkerConfig[], options?: EnsureArMarkersOptions): HTMLElement[];
|
|
97
|
+
declare function resolveArMarkerHint(markerNode: Element | null | undefined, fallbackUrl?: string): ArMarkerHintSummary;
|
|
98
|
+
declare function requestRuntimePermissions(options?: PermissionRequestOptions): Promise<PermissionRequestSummary>;
|
|
99
|
+
|
|
100
|
+
export { type ArMarkerConfig, type ArMarkerDetectionSummary, type ArMarkerHintSummary, type ArRequestParseOptions, type ArRequestSummary, type CatalogObjectEntry, type CatalogProxyLoadOptions, type EnsureArMarkersOptions, type MarkerTrackingHandlers, type PermissionRequestOptions, type PermissionRequestSummary, type PermissionResult, type PrimitiveModelCreateOptions, bindMarkerTracking, createDefaultArMarkers, createMarkersFromCatalog, createPrimitiveModelFromCatalogEntry, ensureArMarkers, extractCatalogEntriesFromPayload, loadCatalogFromProxy, parseArRequestFromSearch, requestRuntimePermissions, resolveArMarkerHint, resolveCatalogProxyUrl, resolveObjectNameForMarker };
|