@needle-tools/engine 4.11.3 → 4.11.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/CHANGELOG.md +4 -0
- package/dist/generateMeshBVH.worker-B9bjdr6J.js +25 -0
- package/dist/{needle-engine.bundle-YQ3I1QD8.umd.cjs → needle-engine.bundle-BxK1-fWD.umd.cjs} +127 -127
- package/dist/{needle-engine.bundle-TscA1IdQ.min.js → needle-engine.bundle-CQdk7IvU.min.js} +131 -131
- package/dist/{needle-engine.bundle-B_aGaFmh.js → needle-engine.bundle-DmMrUPFQ.js} +4668 -4642
- package/dist/needle-engine.js +2 -2
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{vendor-H-9KkM5B.umd.cjs → vendor-6kAXU6fm.umd.cjs} +39 -39
- package/dist/{vendor-BahM12Xj.min.js → vendor-CsyK1CFs.min.js} +46 -46
- package/dist/{vendor-Becub4o1.js → vendor-petGQl0N.js} +3130 -3069
- package/lib/engine/api.d.ts +1 -0
- package/lib/engine/api.js +1 -0
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/engine_utils.d.ts +0 -23
- package/lib/engine/engine_utils.js +0 -202
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/engine_utils_attributes.d.ts +48 -0
- package/lib/engine/engine_utils_attributes.js +70 -0
- package/lib/engine/engine_utils_attributes.js.map +1 -0
- package/lib/engine/engine_utils_qrcode.d.ts +23 -0
- package/lib/engine/engine_utils_qrcode.js +234 -0
- package/lib/engine/engine_utils_qrcode.js.map +1 -0
- package/lib/engine/webcomponents/buttons.js +1 -1
- package/lib/engine/webcomponents/buttons.js.map +1 -1
- package/lib/engine-components/Animation.d.ts +1 -1
- package/lib/engine-components/Animation.js +1 -1
- package/lib/engine-components/AnimationCurve.d.ts +3 -0
- package/lib/engine-components/AnimationCurve.js +3 -0
- package/lib/engine-components/AnimationCurve.js.map +1 -1
- package/lib/engine-components/Animator.d.ts +2 -1
- package/lib/engine-components/Animator.js +2 -1
- package/lib/engine-components/Animator.js.map +1 -1
- package/lib/engine-components/AnimatorController.d.ts +3 -0
- package/lib/engine-components/AnimatorController.js +3 -0
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/LookAtConstraint.d.ts +4 -0
- package/lib/engine-components/LookAtConstraint.js +4 -0
- package/lib/engine-components/LookAtConstraint.js.map +1 -1
- package/lib/engine-components/NeedleMenu.d.ts +1 -0
- package/lib/engine-components/NeedleMenu.js +1 -0
- package/lib/engine-components/NeedleMenu.js.map +1 -1
- package/lib/engine-components/NestedGltf.d.ts +3 -0
- package/lib/engine-components/NestedGltf.js +3 -0
- package/lib/engine-components/NestedGltf.js.map +1 -1
- package/lib/engine-components/ReflectionProbe.d.ts +4 -0
- package/lib/engine-components/ReflectionProbe.js +4 -0
- package/lib/engine-components/ReflectionProbe.js.map +1 -1
- package/lib/engine-components/RendererLightmap.d.ts +6 -0
- package/lib/engine-components/RendererLightmap.js +6 -3
- package/lib/engine-components/RendererLightmap.js.map +1 -1
- package/lib/engine-components/SeeThrough.d.ts +3 -0
- package/lib/engine-components/SeeThrough.js +3 -0
- package/lib/engine-components/SeeThrough.js.map +1 -1
- package/lib/engine-components/Skybox.d.ts +3 -0
- package/lib/engine-components/Skybox.js +3 -0
- package/lib/engine-components/Skybox.js.map +1 -1
- package/lib/engine-components/SpriteRenderer.d.ts +14 -1
- package/lib/engine-components/SpriteRenderer.js +17 -1
- package/lib/engine-components/SpriteRenderer.js.map +1 -1
- package/lib/engine-components/splines/Spline.d.ts +3 -0
- package/lib/engine-components/splines/Spline.js +3 -0
- package/lib/engine-components/splines/Spline.js.map +1 -1
- package/lib/engine-components/splines/SplineUtils.d.ts +3 -0
- package/lib/engine-components/splines/SplineUtils.js +3 -0
- package/lib/engine-components/splines/SplineUtils.js.map +1 -1
- package/lib/engine-components/splines/SplineWalker.d.ts +3 -0
- package/lib/engine-components/splines/SplineWalker.js +3 -0
- package/lib/engine-components/splines/SplineWalker.js.map +1 -1
- package/lib/engine-components/timeline/SignalAsset.d.ts +11 -1
- package/lib/engine-components/timeline/SignalAsset.js +11 -1
- package/lib/engine-components/timeline/SignalAsset.js.map +1 -1
- package/lib/engine-components/ui/Raycaster.d.ts +18 -0
- package/lib/engine-components/ui/Raycaster.js +18 -0
- package/lib/engine-components/ui/Raycaster.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/api.ts +1 -0
- package/src/engine/engine_utils.ts +0 -229
- package/src/engine/engine_utils_attributes.ts +72 -0
- package/src/engine/engine_utils_qrcode.ts +266 -0
- package/src/engine/webcomponents/buttons.ts +1 -1
- package/src/engine-components/Animation.ts +1 -1
- package/src/engine-components/AnimationCurve.ts +4 -1
- package/src/engine-components/Animator.ts +3 -2
- package/src/engine-components/AnimatorController.ts +3 -0
- package/src/engine-components/LookAtConstraint.ts +6 -1
- package/src/engine-components/NeedleMenu.ts +1 -0
- package/src/engine-components/NestedGltf.ts +3 -0
- package/src/engine-components/ReflectionProbe.ts +4 -0
- package/src/engine-components/RendererLightmap.ts +7 -3
- package/src/engine-components/SeeThrough.ts +3 -0
- package/src/engine-components/Skybox.ts +3 -0
- package/src/engine-components/SpriteRenderer.ts +18 -2
- package/src/engine-components/splines/Spline.ts +3 -0
- package/src/engine-components/splines/SplineUtils.ts +3 -1
- package/src/engine-components/splines/SplineWalker.ts +3 -0
- package/src/engine-components/timeline/SignalAsset.ts +13 -2
- package/src/engine-components/ui/Raycaster.ts +19 -0
- package/dist/generateMeshBVH.worker-2qGLkQjg.js +0 -25
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
export namespace InternalAttributeUtils {
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Checks if the given value is considered "falsey" in the context of HTML attributes.
|
|
10
|
+
* A value is considered falsey if it is "0" or "false" (case-insensitive).
|
|
11
|
+
*
|
|
12
|
+
* @param value - The attribute value to check.
|
|
13
|
+
* @returns True if the value is falsey, otherwise false.
|
|
14
|
+
*/
|
|
15
|
+
export function isFalsey(value: string | null): boolean {
|
|
16
|
+
return value === "0" || value?.toLowerCase() === "false";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Retrieves the value of the specified attribute from the given element.
|
|
21
|
+
* If the attribute value is considered falsey, it returns null.
|
|
22
|
+
* @returns The attribute value or null if falsey.
|
|
23
|
+
*/
|
|
24
|
+
export function getAttributeValueIfNotFalsey(element: Element, attributeName: string, opts?: { onAttribute: (value: string) => void }): string | null {
|
|
25
|
+
const attrValue = element.getAttribute(attributeName);
|
|
26
|
+
if (isFalsey(attrValue)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
opts?.onAttribute?.call(null, attrValue!);
|
|
30
|
+
return attrValue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Retrieves the value of the specified attribute from the given element.
|
|
35
|
+
* If the attribute value is considered falsey, it returns false.
|
|
36
|
+
* If the attribute is not set at all, it returns null.
|
|
37
|
+
* @returns The attribute value, false if falsey, or null if not set.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const result = HTMLAttributeUtils.getAttributeAndCheckFalsey(element, 'data-example', {
|
|
42
|
+
* onAttribute: (value, falsey) => {
|
|
43
|
+
* console.log(`Attribute value: ${value}
|
|
44
|
+
* , Is falsey: ${falsey}`);
|
|
45
|
+
* }
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* if (result === false) {
|
|
49
|
+
* console.log('The attribute is set to a falsey value.');
|
|
50
|
+
* } else if (result === null) {
|
|
51
|
+
* console.log('The attribute is not set.');
|
|
52
|
+
* } else {
|
|
53
|
+
* console.log(`The attribute value is: ${result}`);
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function getAttributeAndCheckFalsey(element: Element, attributeName: string, opts?: { onAttribute: (value: string, falsey:boolean) => void }): false | string | null {
|
|
58
|
+
const attrValue = element.getAttribute(attributeName);
|
|
59
|
+
// If the attribute is not set at all we just return
|
|
60
|
+
if(attrValue === null) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
if (isFalsey(attrValue)) {
|
|
64
|
+
opts?.onAttribute?.call(null, attrValue!, true);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
opts?.onAttribute?.call(null, attrValue!, false);
|
|
68
|
+
return attrValue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
|
|
2
|
+
// use for typesafe interface method calls
|
|
3
|
+
import { Quaternion, Vector2, Vector3, Vector4 } from "three";
|
|
4
|
+
|
|
5
|
+
import { needleLogoOnlySVG } from "./assets/index.js";
|
|
6
|
+
import { isDevEnvironment } from "./debug/debug.js";
|
|
7
|
+
import { hasCommercialLicense } from "./engine_license.js";
|
|
8
|
+
import { InternalAttributeUtils } from "./engine_utils_attributes.js";
|
|
9
|
+
import type { NeedleEngineWebComponent } from "./webcomponents/needle-engine.js";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/** Generates a QR code HTML image using https://github.com/davidshimjs/qrcodejs
|
|
14
|
+
* @param args.text The text to encode
|
|
15
|
+
* @param args.width The width of the QR code
|
|
16
|
+
* @param args.height The height of the QR code
|
|
17
|
+
* @param args.colorDark The color of the dark squares
|
|
18
|
+
* @param args.colorLight The color of the light squares
|
|
19
|
+
* @param args.correctLevel The error correction level to use
|
|
20
|
+
* @param args.showLogo If true, the logo will be shown in the center of the QR code. By default the Needle Logo will be used. You can override which logo is being used by setting the `needle-engine` web component's `qr-logo-src` attribute. The logo can also be disabled by setting that attribute to a falsey value (e.g. "0" or "false")
|
|
21
|
+
* @param args.showUrl If true, the URL will be shown below the QR code
|
|
22
|
+
* @param args.domElement The dom element to append the QR code to. If not provided a new div will be created and returned
|
|
23
|
+
* @returns The dom element containing the QR code
|
|
24
|
+
*/
|
|
25
|
+
export async function generateQRCode(args: { domElement?: HTMLElement, text: string, width?: number, height?: number, colorDark?: string, colorLight?: string, correctLevel?: any, showLogo?: boolean, showUrl?: boolean }): Promise<HTMLElement> {
|
|
26
|
+
|
|
27
|
+
// Ensure that the QRCode library is loaded
|
|
28
|
+
if (!globalThis["QRCode"]) {
|
|
29
|
+
const url = "https://cdn.jsdelivr.net/gh/davidshimjs/qrcodejs@gh-pages/qrcode.min.js";
|
|
30
|
+
let script = document.head.querySelector(`script[src="${url}"]`) as HTMLScriptElement;
|
|
31
|
+
if (!script) {
|
|
32
|
+
script = document.createElement("script");
|
|
33
|
+
script.src = url;
|
|
34
|
+
document.head.appendChild(script);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
await new Promise((resolve, _reject) => {
|
|
38
|
+
script.addEventListener("load", () => {
|
|
39
|
+
resolve(true);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const QRCODE = globalThis["QRCode"];
|
|
45
|
+
const target = args.domElement ?? document.createElement("div");
|
|
46
|
+
const qrCode = new QRCODE(target, {
|
|
47
|
+
width: args.width ?? 256,
|
|
48
|
+
height: args.height ?? 256,
|
|
49
|
+
colorDark: "#000000",
|
|
50
|
+
colorLight: "#ffffff",
|
|
51
|
+
correctLevel: args.showLogo ? QRCODE.CorrectionLevel.H : QRCODE.CorrectLevel.M,
|
|
52
|
+
...args,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Number of rows/columns of the generated QR code
|
|
56
|
+
const moduleCount = qrCode?._oQRCode.moduleCount || 0;
|
|
57
|
+
const canvas = qrCode?._oDrawing?._elCanvas as HTMLCanvasElement;
|
|
58
|
+
|
|
59
|
+
let sizePercentage = 0.25;
|
|
60
|
+
if (moduleCount < 40)
|
|
61
|
+
sizePercentage = Math.floor(moduleCount / 4) / moduleCount;
|
|
62
|
+
else
|
|
63
|
+
sizePercentage = Math.floor(moduleCount / 6) / moduleCount;
|
|
64
|
+
|
|
65
|
+
const paddingPercentage = Math.floor(moduleCount / 20) / moduleCount;
|
|
66
|
+
try {
|
|
67
|
+
const img = await internalRenderQRCodeOverlays(canvas, { showLogo: args.showLogo, logoSize: sizePercentage, logoPadding: paddingPercentage }).catch(_e => { /** ignore */ });
|
|
68
|
+
if (img) {
|
|
69
|
+
target.innerHTML = "";
|
|
70
|
+
target.append(img);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch { } // Ignore
|
|
74
|
+
|
|
75
|
+
if (args.showUrl !== false && args.text) {
|
|
76
|
+
// Add link label below the QR code
|
|
77
|
+
// Clean up the text. If it's a URL: remove the protocol, www. part, trailing slashes or trailing question marks
|
|
78
|
+
const existingLabel = target.querySelector(".qr-code-link-label");
|
|
79
|
+
let displayText = args.text.replace(/^(https?:\/\/)?(www\.)?/, "").replace(/\/+$/, "").replace(/\?+$/, "");
|
|
80
|
+
displayText = "Scan to visit " + displayText;
|
|
81
|
+
if (existingLabel) {
|
|
82
|
+
existingLabel.textContent = displayText;
|
|
83
|
+
} else {
|
|
84
|
+
// Create a new label
|
|
85
|
+
const linkLabel = document.createElement("div");
|
|
86
|
+
linkLabel.classList.add("qr-code-link-label");
|
|
87
|
+
args.text = displayText;
|
|
88
|
+
linkLabel.textContent = args.text;
|
|
89
|
+
linkLabel.addEventListener("click", (ev) => {
|
|
90
|
+
// Prevent the QR panel from closing
|
|
91
|
+
ev.stopImmediatePropagation();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
linkLabel.style.textAlign = "center";
|
|
95
|
+
linkLabel.style.fontSize = "0.8em";
|
|
96
|
+
linkLabel.style.marginTop = "0.1em";
|
|
97
|
+
linkLabel.style.color = "#000000";
|
|
98
|
+
linkLabel.style.fontFamily = "'Roboto Flex', sans-serif";
|
|
99
|
+
linkLabel.style.opacity = "0.5";
|
|
100
|
+
linkLabel.style.wordBreak = "break-all";
|
|
101
|
+
linkLabel.style.wordWrap = "break-word";
|
|
102
|
+
linkLabel.style.marginBottom = "0.3em";
|
|
103
|
+
|
|
104
|
+
// Ensure max. width
|
|
105
|
+
target.style.width = "calc(210px + 20px)";
|
|
106
|
+
|
|
107
|
+
target.appendChild(linkLabel);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return target;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
async function internalRenderQRCodeOverlays(canvas: HTMLCanvasElement, args: { showLogo?: boolean, logoSize?: number, logoPadding?: number }): Promise<HTMLImageElement | void> {
|
|
116
|
+
if (!canvas) return;
|
|
117
|
+
|
|
118
|
+
// Internal settings
|
|
119
|
+
const canvasPadding = 8;
|
|
120
|
+
const shadowBlur = 20;
|
|
121
|
+
const rectanglePadding = args.logoPadding || 1. / 32;
|
|
122
|
+
// With dropshadow under the logo
|
|
123
|
+
/*
|
|
124
|
+
const shadowColor = "#00000099";
|
|
125
|
+
const rectangleRadius = 0.4 * 16;
|
|
126
|
+
*/
|
|
127
|
+
// Without dropshadow under the logo
|
|
128
|
+
const shadowColor = "transparent";
|
|
129
|
+
const rectangleRadius = 0;
|
|
130
|
+
|
|
131
|
+
// Draw the website's icon in the center of the QR code
|
|
132
|
+
const image = new Image();
|
|
133
|
+
const element = document.querySelector("needle-engine") as NeedleEngineWebComponent;
|
|
134
|
+
if (!element) {
|
|
135
|
+
console.debug("[QR Code] No web component found")
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const canUseCustomLogo = hasCommercialLicense();
|
|
139
|
+
|
|
140
|
+
// Query logo src from needle-engine attribute.
|
|
141
|
+
// For any supported attribute it's possible to use "falsey" values (e.g. "0" or "false" to disable the logo in the QR code)
|
|
142
|
+
let logoSrc: false | string | null = null;
|
|
143
|
+
logoSrc = InternalAttributeUtils.getAttributeAndCheckFalsey(element, "qrcode-logo-src");
|
|
144
|
+
if (canUseCustomLogo && args.showLogo !== true && logoSrc === false) return; // Explictly disabled
|
|
145
|
+
logoSrc ||= InternalAttributeUtils.getAttributeAndCheckFalsey(element, "logo-src");
|
|
146
|
+
if (canUseCustomLogo && args.showLogo !== true && logoSrc === false) return; // Explicitly disabled
|
|
147
|
+
logoSrc ||= InternalAttributeUtils.getAttributeAndCheckFalsey(element, "loading-logo-src", {
|
|
148
|
+
onAttribute: () => {
|
|
149
|
+
if (isDevEnvironment()) console.warn("[QR Code] 'loading-logo-src' is deprecated, please use 'logo-src' or 'qrcode-logo-src' instead.");
|
|
150
|
+
else console.debug("[QR Code] 'loading-logo-src' is deprecated.");
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
if (canUseCustomLogo && args.showLogo !== true && logoSrc === false) return; // Explicitly disabled
|
|
154
|
+
|
|
155
|
+
if (logoSrc && !canUseCustomLogo) {
|
|
156
|
+
console.warn("[QR Code] Custom logo is only available with a commercial license. Using default Needle logo. Please get a commercial license at https://needle.tools/pricing.");
|
|
157
|
+
logoSrc = null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
logoSrc ||= needleLogoOnlySVG;
|
|
161
|
+
if (!logoSrc) return;
|
|
162
|
+
|
|
163
|
+
let haveLogo = false;
|
|
164
|
+
if (args.showLogo !== false) {
|
|
165
|
+
image.src = logoSrc;
|
|
166
|
+
haveLogo = await new Promise((resolve, _reject) => {
|
|
167
|
+
image.onload = () => resolve(true);
|
|
168
|
+
image.onerror = (err) => {
|
|
169
|
+
let errorUrl = logoSrc !== needleLogoOnlySVG ? "'" + logoSrc + "'" : null;
|
|
170
|
+
console.error("[QR Code] Error loading logo image for QR code", errorUrl, isDevEnvironment() ? err : "");
|
|
171
|
+
resolve(false);
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Add some padding around the canvas – we need to copy the QR code image to a larger canvas
|
|
177
|
+
const paddedCanvas = document.createElement("canvas");
|
|
178
|
+
paddedCanvas.width = canvas.width + canvasPadding;
|
|
179
|
+
paddedCanvas.height = canvas.height + canvasPadding;
|
|
180
|
+
const paddedContext = paddedCanvas.getContext("2d");
|
|
181
|
+
if (!paddedContext) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Clear with white
|
|
186
|
+
paddedContext.fillStyle = "#ffffff";
|
|
187
|
+
paddedContext.fillRect(0, 0, paddedCanvas.width, paddedCanvas.height);
|
|
188
|
+
paddedContext.drawImage(canvas, canvasPadding / 2, canvasPadding / 2);
|
|
189
|
+
|
|
190
|
+
// Enable anti-aliasing
|
|
191
|
+
paddedContext.imageSmoothingEnabled = true;
|
|
192
|
+
paddedContext.imageSmoothingQuality = "high";
|
|
193
|
+
// @ts-ignore
|
|
194
|
+
paddedContext.mozImageSmoothingEnabled = true;
|
|
195
|
+
// @ts-ignore
|
|
196
|
+
paddedContext.webkitImageSmoothingEnabled = true;
|
|
197
|
+
|
|
198
|
+
// Draw a slight gradient background with 10% opacity and "lighten" composite operation
|
|
199
|
+
paddedContext.globalCompositeOperation = "lighten";
|
|
200
|
+
const gradient = paddedContext.createLinearGradient(0, 0, 0, paddedCanvas.height);
|
|
201
|
+
gradient.addColorStop(0, "rgb(45, 45, 45)");
|
|
202
|
+
gradient.addColorStop(1, "rgb(45, 45, 45)");
|
|
203
|
+
paddedContext.fillStyle = gradient;
|
|
204
|
+
paddedContext.fillRect(0, 0, paddedCanvas.width, paddedCanvas.height);
|
|
205
|
+
paddedContext.globalCompositeOperation = "source-over";
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
let sizeX = Math.min(canvas.width, canvas.height) * (args.logoSize || 0.25);
|
|
209
|
+
let sizeY = sizeX;
|
|
210
|
+
|
|
211
|
+
if (haveLogo) {
|
|
212
|
+
// Get aspect of image
|
|
213
|
+
const aspect = image.width / image.height;
|
|
214
|
+
if (aspect > 1) sizeY = sizeX / aspect;
|
|
215
|
+
else sizeX = sizeY * aspect;
|
|
216
|
+
|
|
217
|
+
const rectanglePaddingPx = rectanglePadding * canvas.width;
|
|
218
|
+
|
|
219
|
+
// Apply padding
|
|
220
|
+
const sizeForBackground = Math.max(sizeX, sizeY);
|
|
221
|
+
const sizeXPadded = Math.round(sizeForBackground + rectanglePaddingPx);
|
|
222
|
+
const sizeYPadded = Math.round(sizeForBackground + rectanglePaddingPx);
|
|
223
|
+
const x = (paddedCanvas.width - sizeForBackground) / 2;
|
|
224
|
+
const y = (paddedCanvas.height - sizeForBackground) / 2;
|
|
225
|
+
|
|
226
|
+
// Draw shape with blurred shadow
|
|
227
|
+
paddedContext.shadowColor = shadowColor;
|
|
228
|
+
paddedContext.shadowBlur = shadowBlur;
|
|
229
|
+
|
|
230
|
+
// Draw rounded rectangle with radius
|
|
231
|
+
// Convert 0.4rem to pixels, taking DPI into account
|
|
232
|
+
const radius = rectangleRadius;
|
|
233
|
+
const xPadded = Math.round(x - rectanglePaddingPx / 2);
|
|
234
|
+
const yPadded = Math.round(y - rectanglePaddingPx / 2);
|
|
235
|
+
paddedContext.beginPath();
|
|
236
|
+
paddedContext.moveTo(xPadded + radius, yPadded);
|
|
237
|
+
paddedContext.lineTo(xPadded + sizeXPadded - radius, yPadded);
|
|
238
|
+
paddedContext.quadraticCurveTo(xPadded + sizeXPadded, yPadded, xPadded + sizeXPadded, yPadded + radius);
|
|
239
|
+
paddedContext.lineTo(xPadded + sizeXPadded, yPadded + sizeYPadded - radius);
|
|
240
|
+
paddedContext.quadraticCurveTo(xPadded + sizeXPadded, yPadded + sizeYPadded, xPadded + sizeXPadded - radius, yPadded + sizeYPadded);
|
|
241
|
+
paddedContext.lineTo(xPadded + radius, yPadded + sizeYPadded);
|
|
242
|
+
paddedContext.quadraticCurveTo(xPadded, yPadded + sizeYPadded, xPadded, yPadded + sizeYPadded - radius);
|
|
243
|
+
paddedContext.lineTo(xPadded, yPadded + radius);
|
|
244
|
+
paddedContext.quadraticCurveTo(xPadded, yPadded, xPadded + radius, yPadded);
|
|
245
|
+
paddedContext.fillStyle = "#ffffff";
|
|
246
|
+
paddedContext.closePath();
|
|
247
|
+
paddedContext.fill();
|
|
248
|
+
paddedContext.clip();
|
|
249
|
+
|
|
250
|
+
// Reset shadow and draw favicon
|
|
251
|
+
paddedContext.shadowColor = "transparent";
|
|
252
|
+
const logoX = (paddedCanvas.width - sizeX) / 2;
|
|
253
|
+
const logoY = (paddedCanvas.height - sizeY) / 2;
|
|
254
|
+
paddedContext.drawImage(image, logoX, logoY, sizeX, sizeY);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Replace the canvas with the padded one
|
|
258
|
+
const paddedImage = paddedCanvas.toDataURL("image/png");
|
|
259
|
+
const img = document.createElement("img");
|
|
260
|
+
img.src = paddedImage;
|
|
261
|
+
img.style.width = "100%";
|
|
262
|
+
img.style.height = "auto";
|
|
263
|
+
|
|
264
|
+
return img;
|
|
265
|
+
}
|
|
266
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isDevEnvironment, showBalloonWarning } from "../debug/debug.js";
|
|
2
2
|
import { IContext } from "../engine_types.js";
|
|
3
|
-
import { generateQRCode
|
|
3
|
+
import { generateQRCode } from "../engine_utils_qrcode.js";
|
|
4
4
|
import { onXRSessionEnd, onXRSessionStart } from "../xr/events.js";
|
|
5
5
|
import { getIconElement } from "./icons.js";
|
|
6
6
|
|
|
@@ -58,7 +58,7 @@ declare type AnimationIdentifier = AnimationClip | number | string | undefined;
|
|
|
58
58
|
class Vec2 { x!: number; y!: number }
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
|
-
* Animation component to play animations on a GameObject
|
|
61
|
+
* Animation component to play animations on a GameObject.
|
|
62
62
|
* @category Animation and Sequencing
|
|
63
63
|
* @group Components
|
|
64
64
|
*/
|
|
@@ -27,7 +27,10 @@ export class Keyframe {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* AnimationCurve is a representation of a curve that can be used to animate values over time.
|
|
30
|
+
* AnimationCurve is a representation of a curve that can be used to animate values over time.
|
|
31
|
+
*
|
|
32
|
+
* @category Animation
|
|
33
|
+
* @group Utilities
|
|
31
34
|
*/
|
|
32
35
|
export class AnimationCurve {
|
|
33
36
|
|
|
@@ -39,8 +39,9 @@ export declare class PlayOptions {
|
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* The Animator component plays and manages animations on a GameObject.
|
|
42
|
-
* It works with an AnimatorController to handle state transitions and animation blending.
|
|
43
|
-
* A new AnimatorController can be created from code via `AnimatorController.createFromClips`.
|
|
42
|
+
* It works with an {@link AnimatorController} to handle state transitions and animation blending.
|
|
43
|
+
* A new AnimatorController can be created from code via `AnimatorController.createFromClips`.
|
|
44
|
+
*
|
|
44
45
|
* @category Animation and Sequencing
|
|
45
46
|
* @group Components
|
|
46
47
|
*/
|
|
@@ -51,6 +51,9 @@ declare type CreateAnimatorControllerOptions = {
|
|
|
51
51
|
*
|
|
52
52
|
* Use the static method {@link AnimatorController.createFromClips} to create
|
|
53
53
|
* an animator controller from a set of animation clips.
|
|
54
|
+
*
|
|
55
|
+
* @category Animation and Sequencing
|
|
56
|
+
* @group Utilities
|
|
54
57
|
*/
|
|
55
58
|
export class AnimatorController {
|
|
56
59
|
|
|
@@ -2,9 +2,14 @@ import { Object3D, Vector3 } from "three";
|
|
|
2
2
|
|
|
3
3
|
import { serializable } from "../engine/engine_serialization_decorator.js";
|
|
4
4
|
import { Behaviour } from "./Component.js";
|
|
5
|
+
import type { OrbitControls } from "./OrbitControls.js";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
* A LookAtConstraint is used by OrbitControls to make the camera look at a target.
|
|
8
|
+
* A LookAtConstraint is used by OrbitControls to make the camera look at a target.
|
|
9
|
+
* This component is used by {@link OrbitControls} internally.
|
|
10
|
+
*
|
|
11
|
+
* @category Camera Controls
|
|
12
|
+
* @group Components
|
|
8
13
|
*/
|
|
9
14
|
export class LookAtConstraint extends Behaviour {
|
|
10
15
|
|
|
@@ -10,6 +10,9 @@ const debug = getParam("debugnestedgltf");
|
|
|
10
10
|
|
|
11
11
|
/** The nested gltf is a component that is used to load a gltf file when the component becomes active (start)
|
|
12
12
|
* It will load the gltf file and instantiate it as a child of the parent of the GameObject that has this component
|
|
13
|
+
*
|
|
14
|
+
* @category Asset Management
|
|
15
|
+
* @group Components
|
|
13
16
|
*/
|
|
14
17
|
export class NestedGltf extends Behaviour {
|
|
15
18
|
|
|
@@ -15,6 +15,10 @@ const $reflectionProbeKey = Symbol("reflectionProbeKey");
|
|
|
15
15
|
const $originalMaterial = Symbol("original material");
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
+
* A ReflectionProbe provides reflection data to materials within its defined area.
|
|
19
|
+
*
|
|
20
|
+
* - Sample: http://samples.needle.tools/reflection-probes
|
|
21
|
+
*
|
|
18
22
|
* @category Rendering
|
|
19
23
|
* @group Components
|
|
20
24
|
*/
|
|
@@ -9,9 +9,13 @@ const debug = getParam("debuglightmaps");
|
|
|
9
9
|
|
|
10
10
|
declare type MaterialWithLightmap = Material & { lightMap?: Texture | null };
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* This component is automatically added by the {@link Renderer} component if the object has lightmap uvs AND we have a lightmap.
|
|
15
|
+
*
|
|
16
|
+
* @category Rendering
|
|
17
|
+
* @group Components
|
|
18
|
+
*/
|
|
15
19
|
export class RendererLightmap {
|
|
16
20
|
|
|
17
21
|
get lightmap(): Texture | null {
|
|
@@ -39,6 +39,9 @@ let i = 0;
|
|
|
39
39
|
* Requires a Renderer component on the same object or a child object.
|
|
40
40
|
*
|
|
41
41
|
* - Example https://see-through-walls-z23hmxbz1kjfjn.needle.run/
|
|
42
|
+
*
|
|
43
|
+
* @category Rendering
|
|
44
|
+
* @group Components
|
|
42
45
|
*/
|
|
43
46
|
export class SeeThrough extends Behaviour {
|
|
44
47
|
|
|
@@ -120,6 +120,9 @@ ContextRegistry.registerCallback(ContextEvent.ContextCreationStart, () => {
|
|
|
120
120
|
* ```ts
|
|
121
121
|
* skybox.setSkybox("https://example.com/skybox.hdr");
|
|
122
122
|
* ```
|
|
123
|
+
*
|
|
124
|
+
* @category Rendering
|
|
125
|
+
* @group Components
|
|
123
126
|
*/
|
|
124
127
|
export class RemoteSkybox extends Behaviour {
|
|
125
128
|
|
|
@@ -9,6 +9,9 @@ import { Behaviour } from "./Component.js";
|
|
|
9
9
|
const debug = getParam("debugspriterenderer");
|
|
10
10
|
const showWireframe = getParam("wireframe");
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
12
15
|
class SpriteUtils {
|
|
13
16
|
|
|
14
17
|
static cache: { [key: string]: BufferGeometry } = {};
|
|
@@ -82,7 +85,7 @@ function updateTextureIfNecessary(tex: Texture) {
|
|
|
82
85
|
}
|
|
83
86
|
|
|
84
87
|
/**
|
|
85
|
-
* A sprite is a mesh that represents a 2D image
|
|
88
|
+
* A sprite is a mesh that represents a 2D image. Used by the {@link SpriteRenderer} to render 2D images in the scene.
|
|
86
89
|
* @category Rendering
|
|
87
90
|
* @group Components
|
|
88
91
|
*/
|
|
@@ -152,6 +155,9 @@ export class Sprite {
|
|
|
152
155
|
|
|
153
156
|
const $spriteTexOwner = Symbol("spriteOwner");
|
|
154
157
|
|
|
158
|
+
/**
|
|
159
|
+
* @category Sprites
|
|
160
|
+
*/
|
|
155
161
|
export class SpriteSheet {
|
|
156
162
|
|
|
157
163
|
@serializable(Sprite)
|
|
@@ -162,6 +168,11 @@ export class SpriteSheet {
|
|
|
162
168
|
}
|
|
163
169
|
}
|
|
164
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Used by the {@link SpriteRenderer} to hold the sprite sheet and the currently active sprite index.
|
|
173
|
+
*
|
|
174
|
+
* @category Sprites
|
|
175
|
+
*/
|
|
165
176
|
export class SpriteData {
|
|
166
177
|
|
|
167
178
|
static create() {
|
|
@@ -246,7 +257,12 @@ export class SpriteData {
|
|
|
246
257
|
}
|
|
247
258
|
|
|
248
259
|
/**
|
|
249
|
-
* The sprite renderer renders a sprite on a GameObject using an assigned spritesheet ({@link SpriteData}).
|
|
260
|
+
* The sprite renderer renders a sprite on a GameObject using an assigned spritesheet ({@link SpriteData}).
|
|
261
|
+
*
|
|
262
|
+
* - Example: https://engine.needle.tools/samples/spritesheet-animation
|
|
263
|
+
*
|
|
264
|
+
* @category Sprites
|
|
265
|
+
* @group Components
|
|
250
266
|
*/
|
|
251
267
|
export class SpriteRenderer extends Behaviour {
|
|
252
268
|
|
|
@@ -36,6 +36,9 @@ export class SplineData {
|
|
|
36
36
|
* The spline is defined by an array of knots (SplineData) which define position, rotation and tangents.
|
|
37
37
|
*
|
|
38
38
|
* You can create a SplineContainer from an array of points using the static method 'createFromPoints'.
|
|
39
|
+
*
|
|
40
|
+
* @category Splines
|
|
41
|
+
* @group Components
|
|
39
42
|
*/
|
|
40
43
|
export class SplineContainer extends Behaviour {
|
|
41
44
|
|
|
@@ -11,6 +11,9 @@ import { SplineContainer } from "./Spline.js";
|
|
|
11
11
|
* Use this with a SplineContainer component.
|
|
12
12
|
*
|
|
13
13
|
* - Example http://samples.needle.tools/splines
|
|
14
|
+
*
|
|
15
|
+
* @category Splines
|
|
16
|
+
* @group Components
|
|
14
17
|
*/
|
|
15
18
|
export class SplineWalker extends Behaviour {
|
|
16
19
|
|
|
@@ -2,15 +2,23 @@ import { serializable } from "../../engine/engine_serialization_decorator.js";
|
|
|
2
2
|
import { getParam } from "../../engine/engine_utils.js";
|
|
3
3
|
import { Behaviour } from "../Component.js";
|
|
4
4
|
import { EventList } from "../EventList.js";
|
|
5
|
+
import type { PlayableDirector } from "./PlayableDirector.js";
|
|
5
6
|
|
|
6
7
|
const debug = getParam("debugsignals")
|
|
7
8
|
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Used to reference a signal asset in a SignalReceiver. This is internally used by the {@link SignalReceiverEvent}.
|
|
12
|
+
*/
|
|
9
13
|
export class SignalAsset {
|
|
10
14
|
@serializable()
|
|
11
15
|
guid!: string;
|
|
12
16
|
}
|
|
13
17
|
|
|
18
|
+
/**
|
|
19
|
+
* An event that links a signal to a reaction.
|
|
20
|
+
* Used internally by {@link SignalReceiver}.
|
|
21
|
+
*/
|
|
14
22
|
export class SignalReceiverEvent {
|
|
15
23
|
@serializable(SignalAsset)
|
|
16
24
|
signal!: SignalAsset;
|
|
@@ -19,7 +27,10 @@ export class SignalReceiverEvent {
|
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
/** SignalReceiver is a component that listens for signals and invokes a reaction when a signal is received.
|
|
22
|
-
* Signals can be added to a signal track on a
|
|
30
|
+
* Signals can be added to a signal track on a {@link PlayableDirector}
|
|
31
|
+
*
|
|
32
|
+
* @category Sequencing and Timelines
|
|
33
|
+
* @group Components
|
|
23
34
|
*/
|
|
24
35
|
export class SignalReceiver extends Behaviour {
|
|
25
36
|
|
|
@@ -41,7 +52,7 @@ export class SignalReceiver extends Behaviour {
|
|
|
41
52
|
|
|
42
53
|
/** @internal */
|
|
43
54
|
awake(): void {
|
|
44
|
-
if(debug) console.log("SignalReceiver awake", this);
|
|
55
|
+
if (debug) console.log("SignalReceiver awake", this);
|
|
45
56
|
}
|
|
46
57
|
|
|
47
58
|
/** @internal */
|
|
@@ -28,6 +28,12 @@ export abstract class Raycaster extends Behaviour {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* A Raycaster that performs raycasting against its own GameObject.
|
|
33
|
+
*
|
|
34
|
+
* @category Interaction
|
|
35
|
+
* @group Components
|
|
36
|
+
*/
|
|
31
37
|
export class ObjectRaycaster extends Raycaster {
|
|
32
38
|
private targets: Object3D[] | null = null;
|
|
33
39
|
private raycastHits: Intersection[] = [];
|
|
@@ -65,6 +71,13 @@ export class ObjectRaycaster extends Raycaster {
|
|
|
65
71
|
}
|
|
66
72
|
}
|
|
67
73
|
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* A Raycaster that performs raycasting against UI elements (objects with a CanvasRenderer component).
|
|
77
|
+
*
|
|
78
|
+
* @category UI
|
|
79
|
+
* @group Components
|
|
80
|
+
*/
|
|
68
81
|
export class GraphicRaycaster extends ObjectRaycaster {
|
|
69
82
|
// eventCamera: Camera | null = null;
|
|
70
83
|
// ignoreReversedGraphics: boolean = false;
|
|
@@ -76,6 +89,12 @@ export class GraphicRaycaster extends ObjectRaycaster {
|
|
|
76
89
|
}
|
|
77
90
|
}
|
|
78
91
|
|
|
92
|
+
/**
|
|
93
|
+
* A Raycaster that performs sphere overlap raycasting for spatial grab interactions in XR.
|
|
94
|
+
*
|
|
95
|
+
* @category XR
|
|
96
|
+
* @group Components
|
|
97
|
+
*/
|
|
79
98
|
export class SpatialGrabRaycaster extends Raycaster {
|
|
80
99
|
|
|
81
100
|
/**
|