Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, POST, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type"
3D:
get other shapes
VIEWER:
add env to base & claws
analyze sample code
cubemap:
Ideal Cubemap for Jewelry: A black/white high-contrast studio HDRI or neutral cubemap
white gold surface reflects mostly white or gray tones
out-of-focus bright spots on a neutral gray background—perfect for capturing sparkle without introducing color noise
HOMENEW:
see what can be preloaded for viewer
lujo re-order form
SERVER:
LoadModule headers_module modules/mod_headers.so
So I should change this:
AllowOverride None
Options None
Require all granted
to:
AllowOverride None
Options None
Require all granted
Header set Access-Control-Allow-Origin "https://b2b.beverlyhillsjewellers.ca"
Header set Access-Control-Allow-Methods "GET, POST, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type"
test with curl:
curl -I https://b2b.beverlyhillsjewellers.ca/images/stl/studio_small_03_1k.hdr
You should see this in the output:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type
✅ Option 1: Use Stats.js (Easy FPS monitor for Three.js)
stats.js is a lightweight library that shows real-time FPS in a corner of your canvas.
🔧 Steps to add it:
Add the import (before your animate() call):
import Stats from 'https://esm.sh/three@0.161.0/examples/jsm/libs/stats.module.js';
Initialize and add it to the DOM:
const stats = new Stats();
document.body.appendChild(stats.dom);
Inside your animate() loop, add:
stats.update();
✅ Option 2: Measure FPS manually
If you don't want to use external libraries, you can measure FPS like this:
let lastTime = performance.now();
let frames = 0;
function animate() {
requestAnimationFrame(animate);
const now = performance.now();
frames++;
if (now - lastTime >= 1000) { // Every 1 second
console.log(`FPS: ${frames}`);
frames = 0;
lastTime = now;
}
// Your animation
if (ETspinEnabled) {
assembly.rotation.y += 0.01;
}
controls.update();
renderer.render(scene, camera);
}
animate();
This prints the current FPS to the browser console every second.
⚙️ Bonus: Make animation frame-rate independent
If you want consistent speed on all systems, multiply the rotation by deltaTime:
let clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta(); // time in seconds since last frame
if (ETspinEnabled) {
assembly.rotation.y += delta * 1.0; // Spin at 1 rad/sec (adjust as needed)
}
controls.update();
renderer.render(scene, camera);
}
FRAGMENT SHADER:
https://aurigait.com/blog/how-to-create-custom-shaders-for-a-diamond-in-three-js/
SERVING FILES LOCALLY:
To store Three.js locally and serve it from your own server instead of using a CDN like https://esm.sh, you need to download the correct ES module build of Three.js and reference it with a local path.
Here’s how to do it:
✅ Step-by-step:
1. Download the ES module build
Go to the official GitHub repository:
📦 https://github.com/mrdoob/three.js/
Navigate to:
three.js/build/three.module.js
Or directly download the minified ES module here:
🔗 three.module.js (v0.161.0)
You can also grab it via terminal:
curl https://unpkg.com/three@0.161.0/build/three.module.js -o three.module.js
2. Save it in your project folder
Example directory:
/your-server-root/
index.html
/js/
three.module.js
3. Update your import
Now reference the local file instead of the online version:
import * as THREE from './js/three.module.js';
Make sure your web server allows module loading from that path (i.e. no MIME errors).
⚠️ If you use loaders (like STLLoader)
They are also modules and depend on the core Three module. Example:
import { STLLoader } from './js/loaders/STLLoader.js';
Make sure to adjust paths inside the loader if needed (they often import from 'three', which you might need to patch to './three.module.js').
✅ Recap:
Download three.module.js locally.
Reference it in your HTML or JS like:
import * as THREE from './js/three.module.js';
Repeat for other Three.js files you use (loaders, controls, etc.).
SCALE SETTINGS:
✅ Goal:
You want to double the overall size (R) without changing r or height.
🔧 How to Do It (with STL Geometry in Three.js):
Assuming you loaded your STL into a BufferGeometry (e.g., using STLLoader):
// 1. Center the geometry to make scaling symmetrical
geometry.computeBoundingBox();
geometry.center();
// 2. Create a scaling matrix
const scaleMatrix = new THREE.Matrix4().makeScale(2, 1, 2); // X and Z doubled, Y unchanged
// 3. Apply the scaling
geometry.applyMatrix4(scaleMatrix);
// 4. Recalculate normals and bounding info
geometry.computeVertexNormals();
geometry.computeBoundingBox();
geometry.computeBoundingSphere();
This stretches the donut outward (twice as wide/long) without affecting:
Material thickness (tube size remains same).
Height (Y axis untouched).
🧪 Visual Example (Conceptually)
Original torus:
R = 5, r = 1
After transformation:
R = 10, r = 1
You just "pull" the ring outward, but don’t thicken the material.
📝 STL File Directly?
If you want to modify the STL before loading (e.g., preprocessing), you’d:
Load vertices.
For each vertex (x, y, z):
x *= 2;
z *= 2;
Save new STL.
If I have already loaded the stl file into gTriangles(), will these functions work as expected?
TYPE VERTEX
x AS SINGLE
y AS SINGLE
z AS SINGLE
END TYPE
TYPE TRIANGLE
normal AS VERTEX
v1 AS VERTEX
v2 AS VERTEX
v3 AS VERTEX
END TYPE
GLOBAL gTriangles() AS TRIANGLE
GLOBAL gTriangleCount,origCount AS LONG
LOCAL cx AS VERTEX
cx = GetCenter()
CALL ScaleFromCenter(cx, scale)
' === Function to compute geometric center ===
FUNCTION GetCenter() AS VERTEX
LOCAL cx, cy, cz AS DOUBLE
DIM result AS VERTEX
FOR i = 0 TO origCount - 1
cx = cx + gTriangles(i).v1.x + gTriangles(i).v2.x + gTriangles(i).v3.x
cy = cy + gTriangles(i).v1.y + gTriangles(i).v2.y + gTriangles(i).v3.y
cz = cz + gTriangles(i).v1.z + gTriangles(i).v2.z + gTriangles(i).v3.z
NEXT
result.X = cx / (origCount * 3)
result.Y = cy / (origCount * 3)
result.Z = cz / (origCount * 3)
FUNCTION = result
END FUNCTION
' === Function to scale outward from center ===
SUB ScaleFromCenter(BYVAL center AS VERTEX, BYVAL scaleFactor AS DOUBLE)
LOCAL dx, dy, dz AS DOUBLE
LOCAL len AS DOUBLE
FOR i = 0 TO origCount - 1
' ---- v1 ----
dx = gTriangles(i).v1.x - center.x
dy = gTriangles(i).v1.y - center.y
dz = gTriangles(i).v1.z - center.z
' Compute direction vector length
len = SQR(dx*dx + dy*dy + dz*dz)
IF len > 0 THEN
dx = dx / len: dy = dy / len: dz = dz / len
' Move vertex outward from center
gTriangles(i).v1.x = center.X + dx * (len * scaleFactor)
gTriangles(i).v1.y = center.Y + dy * (len * scaleFactor)
gTriangles(i).v1.z = center.Z + dz * (len * scaleFactor)
END IF
' ---- v2 ----
dx = gTriangles(i).v2.x - center.x
dy = gTriangles(i).v2.y - center.y
dz = gTriangles(i).v2.z - center.z
' Compute direction vector length
len = SQR(dx*dx + dy*dy + dz*dz)
IF len > 0 THEN
dx = dx / len: dy = dy / len: dz = dz / len
' Move vertex outward from center
gTriangles(i).v2.x = center.X + dx * (len * scaleFactor)
gTriangles(i).v2.y = center.Y + dy * (len * scaleFactor)
gTriangles(i).v2.z = center.Z + dz * (len * scaleFactor)
END IF
' ---- v3 ----
dx = gTriangles(i).v3.x - center.x
dy = gTriangles(i).v3.y - center.y
dz = gTriangles(i).v3.z - center.z
' Compute direction vector length
len = SQR(dx*dx + dy*dy + dz*dz)
IF len > 0 THEN
dx = dx / len: dy = dy / len: dz = dz / len
' Move vertex outward from center
gTriangles(i).v3.x = center.X + dx * (len * scaleFactor)
gTriangles(i).v3.y = center.Y + dy * (len * scaleFactor)
gTriangles(i).v3.z = center.Z + dz * (len * scaleFactor)
END IF
NEXT
END SUB
If you want the parent to expose variables to the iframe if both are from
the same origin, assign them like:
window.someValue = ...;
Then from the iframe:
const val = parent.someValue;
If iframe changes parent.someValue it also changes window.someValue on parent
If different origins:
To send data to iframe:
In the parent (e.g., on parent.com):
const iframe = document.querySelector("iframe");
iframe.contentWindow.postMessage({ type: "send-x", value: window.x }, "https://iframe.com");
In the iframe (e.g., on iframe.com):
window.addEventListener("message", (event) => {
if (event.origin !== "https://parent.com") return; // Validate origin
if (event.data.type === "send-x") {
console.log("Received x from parent:", event.data.value);
}
});
To send data to parent:
In the parent page:
window.addEventListener("message", (event) => {
if (event.origin !== window.location.origin) return; // Optional for security
if (event.data.type === "update") {
window.someValue = event.data.newValue;
console.log("Updated by iframe:", window.someValue);
}
});
In the iframe (e.g., on iframe.com):
parent.postMessage({ type: "update", newValue: 6 }, "*");
✅ Corrected Working Version
Here’s a complete example to illustrate how to do it step by step using THREE.Clock and animation interpolation:
🔹 Step 1: Store the original position/rotation after loading
Do this once, right after your model is placed in its starting orientation:
const originalPosition = new THREE.Vector3(0, 0, 0);
const originalRotation = new THREE.Euler(0, Math.PI / 4, 0); // 45° around Y
ETSpinGroup.position.copy(originalPosition);
ETSpinGroup.rotation.copy(originalRotation);
🔹 Step 2: Variables for animation state
let resetting = false;
let resetProgress = 0;
const resetDuration = 1.0; // seconds
let startPosition, startRotation;
const clock = new THREE.Clock(); // needed for smooth time-based animation
🔹 Step 3: Handle the spin toggle button
When the user enables spinning, start the reset animation first:
document.getElementById('ETspinButton').addEventListener('click', () => {
if (!ETspinEnabled) {
// Save current state
startPosition = ETSpinGroup.position.clone();
startRotation = ETSpinGroup.rotation.clone();
resetting = true;
resetProgress = 0;
clock.start(); // restart clock to get clean delta time
}
ETspinEnabled = true;
});
🔹 Step 4: Animate the reset in animate()
In your animate() loop:
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
if (resetting) {
resetProgress += delta / resetDuration;
const t = Math.min(resetProgress, 1.0);
// Interpolate position
ETSpinGroup.position.lerpVectors(startPosition, originalPosition, t);
// Interpolate rotation
ETSpinGroup.rotation.x = THREE.MathUtils.lerp(startRotation.x, originalRotation.x, t);
ETSpinGroup.rotation.y = THREE.MathUtils.lerp(startRotation.y, originalRotation.y, t);
ETSpinGroup.rotation.z = THREE.MathUtils.lerp(startRotation.z, originalRotation.z, t);
if (t >= 1.0) {
resetting = false;
cycle = 1; // start actual spin logic now
}
} else if (ETspinEnabled) {
// Your existing spin logic
ETSpinGroup.rotation.y += 0.01; // or however you spin it
}
controls.update();
renderer.render(scene, camera);
}