bilca commited on
Commit
5fd1402
·
verified ·
1 Parent(s): 77b6809

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +15 -46
index.js CHANGED
@@ -1,10 +1,9 @@
1
  (function() {
2
- // --- Outer scope variables for camera state and previous position ---
3
  let cameraInstance = null;
4
  let controlsInstance = null;
5
  let initialCameraPosition = null;
6
  let initialCameraRotation = null;
7
- let prevCameraPos = null;
8
 
9
  // Helper: Get query parameters from THIS script’s src URL.
10
  function getScriptQueryParam(param) {
@@ -30,7 +29,8 @@
30
  var maxAngle = parseFloat(getScriptQueryParam("maxAngle") || "360");
31
 
32
  // Optional parameters for translation limits.
33
- // Defaults: for example, -10 to 10 for each axis.
 
34
  var minX = parseFloat(getScriptQueryParam("minX") || "-10");
35
  var maxX = parseFloat(getScriptQueryParam("maxX") || "10");
36
  var minY = parseFloat(getScriptQueryParam("minY") || "-10");
@@ -160,7 +160,6 @@
160
  document.head.appendChild(styleEl);
161
 
162
  // Create the widget container and set its inner HTML.
163
- // Added new reset button with id "reset-camera-btn" displaying the 🗘 emoji.
164
  var widgetContainer = document.createElement('div');
165
  widgetContainer.id = 'ply-widget-container';
166
  widgetContainer.innerHTML = `
@@ -185,7 +184,6 @@
185
  </div>
186
  </div>
187
  `;
188
- // Append widgetContainer to the current script's parent so it appears in place.
189
  document.currentScript.parentNode.appendChild(widgetContainer);
190
 
191
  // Grab element references.
@@ -208,40 +206,32 @@
208
 
209
  // --- Button Event Handlers ---
210
 
211
- // When the preview image is clicked, hide it, show the viewer, and initialize the 3D viewer.
212
  gifPreview.addEventListener('click', function() {
213
  gifPreview.style.display = 'none';
214
  viewerContainer.style.display = 'block';
215
  initializeViewer();
216
  });
217
 
218
- // Close button: hide the viewer and show the preview.
219
  closeBtn.addEventListener('click', function() {
220
- // Exit fullscreen if active.
221
  if (document.fullscreenElement === widgetContainer) {
222
  if (document.exitFullscreen) {
223
  document.exitFullscreen();
224
  }
225
  }
226
- // Remove fake-fullscreen class (for iOS) if present.
227
  widgetContainer.classList.remove('fake-fullscreen');
228
  viewerContainer.style.display = 'none';
229
  gifPreview.style.display = 'block';
230
  });
231
 
232
- // Fullscreen toggle: use native Fullscreen API if available; otherwise, for iOS, toggle a CSS-based fullscreen.
233
  fullscreenToggle.addEventListener('click', function() {
234
  if (isIOS) {
235
- // Toggle fake fullscreen via CSS on iOS.
236
  if (!widgetContainer.classList.contains('fake-fullscreen')) {
237
  widgetContainer.classList.add('fake-fullscreen');
238
  } else {
239
  widgetContainer.classList.remove('fake-fullscreen');
240
  }
241
- // Update icon based on state.
242
  fullscreenToggle.textContent = widgetContainer.classList.contains('fake-fullscreen') ? '⇲' : '⇱';
243
  } else {
244
- // Non-iOS: use standard Fullscreen API.
245
  if (!document.fullscreenElement) {
246
  if (widgetContainer.requestFullscreen) {
247
  widgetContainer.requestFullscreen();
@@ -260,7 +250,6 @@
260
  }
261
  });
262
 
263
- // Update the fullscreen button icon on fullscreen change (for non-iOS browsers).
264
  document.addEventListener('fullscreenchange', function() {
265
  if (document.fullscreenElement === widgetContainer) {
266
  fullscreenToggle.textContent = '⇲';
@@ -269,17 +258,14 @@
269
  }
270
  });
271
 
272
- // Help (instructions) toggle: show/hide the instructions.
273
  helpToggle.addEventListener('click', function(e) {
274
  e.stopPropagation();
275
  menuContent.style.display = (menuContent.style.display === 'block') ? 'none' : 'block';
276
  });
277
 
278
- // Reset Camera button: restore the camera to its initial state.
279
  resetCameraBtn.addEventListener('click', function() {
280
  console.log("Reset camera button clicked.");
281
  if (cameraInstance && initialCameraPosition && initialCameraRotation) {
282
- // Reset camera's position and rotation using clone().
283
  cameraInstance.position = initialCameraPosition.clone();
284
  cameraInstance.rotation = initialCameraRotation.clone();
285
  if (typeof cameraInstance.update === 'function') {
@@ -293,25 +279,17 @@
293
 
294
  // --- Initialize the 3D PLY Viewer ---
295
  async function initializeViewer() {
296
- // Dynamically import the gsplat library.
297
  const SPLAT = await import("https://cdn.jsdelivr.net/npm/gsplat@latest");
298
-
299
- // Display the progress dialog.
300
  progressDialog.style.display = 'block';
301
-
302
- // Create renderer, scene, camera, and controls.
303
  const renderer = new SPLAT.WebGLRenderer(canvas);
304
  const scene = new SPLAT.Scene();
305
  const camera = new SPLAT.Camera();
306
  const controls = new SPLAT.OrbitControls(camera, canvas);
307
 
308
- // Store the camera and controls and capture their initial state using clone().
309
  cameraInstance = camera;
310
  controlsInstance = controls;
311
  initialCameraPosition = camera.position.clone();
312
  initialCameraRotation = camera.rotation.clone();
313
- // Initialize previous camera position as the initial state.
314
- prevCameraPos = camera.position.clone();
315
 
316
  canvas.style.background = "#FEFEFD";
317
  controls.maxZoom = maxZoom;
@@ -321,7 +299,6 @@
321
 
322
  controls.update();
323
 
324
- // Load the PLY model from the provided URL.
325
  try {
326
  await SPLAT.PLYLoader.LoadAsync(
327
  plyUrl,
@@ -330,39 +307,31 @@
330
  progressIndicator.value = progress * 100;
331
  }
332
  );
333
- // Hide the progress dialog once the model is loaded.
334
  progressDialog.style.display = 'none';
335
  } catch (error) {
336
  console.error("Error loading PLY file:", error);
337
  progressDialog.innerHTML = `<p style="color: red">Error loading model: ${error.message}</p>`;
338
  }
339
 
340
- // Render loop and resize handling.
341
- const handleResize = () => {
342
- renderer.setSize(canvas.clientWidth, canvas.clientHeight);
343
- };
344
 
345
  const frame = () => {
346
- // Save the current camera position
347
- let currentPos = camera.position.clone();
348
  controls.update();
349
-
350
- // Check if any axis exceeds its allowed limits.
351
- if (currentPos.x < minX || currentPos.x > maxX ||
352
- currentPos.y < minY || currentPos.y > maxY ||
353
- currentPos.z < minZ || currentPos.z > maxZ) {
354
- // Restore previous allowed position.
355
- camera.position = prevCameraPos.clone();
356
- // Optionally, you could also update controlsInstance target here if accessible.
357
- } else {
358
- // Otherwise, update prevCameraPos.
359
- prevCameraPos = camera.position.clone();
360
- }
361
-
362
  renderer.render(scene, camera);
363
  requestAnimationFrame(frame);
364
  };
365
 
 
 
 
 
366
  handleResize();
367
  window.addEventListener("resize", handleResize);
368
  requestAnimationFrame(frame);
 
1
  (function() {
2
+ // --- Outer scope variables for camera state ---
3
  let cameraInstance = null;
4
  let controlsInstance = null;
5
  let initialCameraPosition = null;
6
  let initialCameraRotation = null;
 
7
 
8
  // Helper: Get query parameters from THIS script’s src URL.
9
  function getScriptQueryParam(param) {
 
29
  var maxAngle = parseFloat(getScriptQueryParam("maxAngle") || "360");
30
 
31
  // Optional parameters for translation limits.
32
+ // For this example, you can pass minX, maxX, minY, maxY, minZ, maxZ.
33
+ // If not provided, default to some values (e.g., -10 to 10).
34
  var minX = parseFloat(getScriptQueryParam("minX") || "-10");
35
  var maxX = parseFloat(getScriptQueryParam("maxX") || "10");
36
  var minY = parseFloat(getScriptQueryParam("minY") || "-10");
 
160
  document.head.appendChild(styleEl);
161
 
162
  // Create the widget container and set its inner HTML.
 
163
  var widgetContainer = document.createElement('div');
164
  widgetContainer.id = 'ply-widget-container';
165
  widgetContainer.innerHTML = `
 
184
  </div>
185
  </div>
186
  `;
 
187
  document.currentScript.parentNode.appendChild(widgetContainer);
188
 
189
  // Grab element references.
 
206
 
207
  // --- Button Event Handlers ---
208
 
 
209
  gifPreview.addEventListener('click', function() {
210
  gifPreview.style.display = 'none';
211
  viewerContainer.style.display = 'block';
212
  initializeViewer();
213
  });
214
 
 
215
  closeBtn.addEventListener('click', function() {
 
216
  if (document.fullscreenElement === widgetContainer) {
217
  if (document.exitFullscreen) {
218
  document.exitFullscreen();
219
  }
220
  }
 
221
  widgetContainer.classList.remove('fake-fullscreen');
222
  viewerContainer.style.display = 'none';
223
  gifPreview.style.display = 'block';
224
  });
225
 
 
226
  fullscreenToggle.addEventListener('click', function() {
227
  if (isIOS) {
 
228
  if (!widgetContainer.classList.contains('fake-fullscreen')) {
229
  widgetContainer.classList.add('fake-fullscreen');
230
  } else {
231
  widgetContainer.classList.remove('fake-fullscreen');
232
  }
 
233
  fullscreenToggle.textContent = widgetContainer.classList.contains('fake-fullscreen') ? '⇲' : '⇱';
234
  } else {
 
235
  if (!document.fullscreenElement) {
236
  if (widgetContainer.requestFullscreen) {
237
  widgetContainer.requestFullscreen();
 
250
  }
251
  });
252
 
 
253
  document.addEventListener('fullscreenchange', function() {
254
  if (document.fullscreenElement === widgetContainer) {
255
  fullscreenToggle.textContent = '⇲';
 
258
  }
259
  });
260
 
 
261
  helpToggle.addEventListener('click', function(e) {
262
  e.stopPropagation();
263
  menuContent.style.display = (menuContent.style.display === 'block') ? 'none' : 'block';
264
  });
265
 
 
266
  resetCameraBtn.addEventListener('click', function() {
267
  console.log("Reset camera button clicked.");
268
  if (cameraInstance && initialCameraPosition && initialCameraRotation) {
 
269
  cameraInstance.position = initialCameraPosition.clone();
270
  cameraInstance.rotation = initialCameraRotation.clone();
271
  if (typeof cameraInstance.update === 'function') {
 
279
 
280
  // --- Initialize the 3D PLY Viewer ---
281
  async function initializeViewer() {
 
282
  const SPLAT = await import("https://cdn.jsdelivr.net/npm/gsplat@latest");
 
 
283
  progressDialog.style.display = 'block';
 
 
284
  const renderer = new SPLAT.WebGLRenderer(canvas);
285
  const scene = new SPLAT.Scene();
286
  const camera = new SPLAT.Camera();
287
  const controls = new SPLAT.OrbitControls(camera, canvas);
288
 
 
289
  cameraInstance = camera;
290
  controlsInstance = controls;
291
  initialCameraPosition = camera.position.clone();
292
  initialCameraRotation = camera.rotation.clone();
 
 
293
 
294
  canvas.style.background = "#FEFEFD";
295
  controls.maxZoom = maxZoom;
 
299
 
300
  controls.update();
301
 
 
302
  try {
303
  await SPLAT.PLYLoader.LoadAsync(
304
  plyUrl,
 
307
  progressIndicator.value = progress * 100;
308
  }
309
  );
 
310
  progressDialog.style.display = 'none';
311
  } catch (error) {
312
  console.error("Error loading PLY file:", error);
313
  progressDialog.innerHTML = `<p style="color: red">Error loading model: ${error.message}</p>`;
314
  }
315
 
316
+ // A simple clamp function.
317
+ function clamp(val, min, max) {
318
+ return Math.min(Math.max(val, min), max);
319
+ }
320
 
321
  const frame = () => {
 
 
322
  controls.update();
323
+ // Clamp each axis individually.
324
+ camera.position.x = clamp(camera.position.x, minX, maxX);
325
+ camera.position.y = clamp(camera.position.y, minY, maxY);
326
+ camera.position.z = clamp(camera.position.z, minZ, maxZ);
 
 
 
 
 
 
 
 
 
327
  renderer.render(scene, camera);
328
  requestAnimationFrame(frame);
329
  };
330
 
331
+ const handleResize = () => {
332
+ renderer.setSize(canvas.clientWidth, canvas.clientHeight);
333
+ };
334
+
335
  handleResize();
336
  window.addEventListener("resize", handleResize);
337
  requestAnimationFrame(frame);