folder rename

This commit is contained in:
gsinghpal
2026-04-16 20:53:53 -04:00
parent 3f3ddcbab4
commit 7c7ef06057
634 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,237 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>3D Part Viewer</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
html,body{width:100%;height:100%;overflow:hidden;font-family:system-ui,-apple-system,sans-serif;background:#f0f2f5}
#viewer-container{width:100%;height:100%}
#loading{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;color:#6c757d;z-index:100}
#loading .spinner{width:44px;height:44px;border:3px solid #dee2e6;border-top-color:#0d6efd;border-radius:50%;animation:spin .8s linear infinite;margin:0 auto 12px}
@keyframes spin{to{transform:rotate(360deg)}}
#error{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background:#fff3cd;border:1px solid #ffc107;border-radius:8px;padding:20px 28px;color:#664d03;max-width:80%;text-align:center;font-size:13px;z-index:100;display:none}
#format-badge{position:absolute;top:10px;right:10px;font-size:11px;font-weight:600;padding:4px 10px;border-radius:4px;z-index:100;backdrop-filter:blur(4px)}
.fmt-step{background:rgba(33,150,243,.15);color:#1565c0}
.fmt-iges{background:rgba(156,39,176,.15);color:#7b1fa2}
.fmt-stl{background:rgba(76,175,80,.15);color:#2e7d32}
.fmt-brep{background:rgba(255,152,0,.15);color:#e65100}
.fmt-other{background:rgba(158,158,158,.15);color:#616161}
#toolbar{position:absolute;top:10px;left:10px;display:flex;gap:4px;z-index:100;flex-wrap:wrap;background:rgba(255,255,255,.85);padding:4px;border-radius:6px;backdrop-filter:blur(4px);box-shadow:0 1px 3px rgba(0,0,0,.1)}
#toolbar button{background:#fff;border:1px solid #ced4da;border-radius:4px;padding:4px 8px;font-size:11px;font-weight:500;cursor:pointer;color:#495057;transition:all .15s;min-width:40px}
#toolbar button:hover{background:#0d6efd;color:#fff;border-color:#0d6efd}
#toolbar .btn-divider{width:1px;background:#dee2e6;margin:2px 4px}
</style>
</head>
<body>
<div id="viewer-container"></div>
<div id="toolbar">
<button onclick="setView('top')" title="Top view">Top</button>
<button onclick="setView('bottom')" title="Bottom view">Btm</button>
<button onclick="setView('front')" title="Front view">Front</button>
<button onclick="setView('back')" title="Back view">Back</button>
<button onclick="setView('left')" title="Left view">Left</button>
<button onclick="setView('right')" title="Right view">Right</button>
<button onclick="setView('iso')" title="Isometric view">Iso</button>
<span class="btn-divider"></span>
<button onclick="fitToView()" title="Fit to view">Fit</button>
<button onclick="takeScreenshot()" title="Take screenshot (PNG)">📷</button>
</div>
<div id="format-badge"></div>
<div id="loading"><div class="spinner"></div><div id="loading-msg">Loading 3D model...</div></div>
<div id="error"></div>
<script src="/fusion_plating_configurator/static/lib/o3dv/o3dv.min.js"></script>
<script>
(function() {
const container = document.getElementById('viewer-container');
const loadingEl = document.getElementById('loading');
const loadingMsg = document.getElementById('loading-msg');
const errorEl = document.getElementById('error');
const fmtBadge = document.getElementById('format-badge');
const params = new URLSearchParams(window.location.search);
const attachmentId = params.get('id');
const fileName = params.get('name') || 'model.stl';
function detectFormat(name) {
if (!name) return 'other';
const n = name.toLowerCase();
if (n.match(/\.(step|stp)$/)) return 'step';
if (n.match(/\.(iges|igs)$/)) return 'iges';
if (n.match(/\.(brep|brp)$/)) return 'brep';
if (n.match(/\.stl$/)) return 'stl';
if (n.match(/\.(obj)$/)) return 'other';
if (n.match(/\.(gltf|glb)$/)) return 'other';
if (n.match(/\.(3ds|fbx|dae|3mf|ply|off|wrl|3dm)$/)) return 'other';
return 'other';
}
function showFormat(fmt) {
fmtBadge.className = 'fmt-' + fmt;
fmtBadge.textContent = fmt.toUpperCase();
}
function showError(msg) {
loadingEl.style.display = 'none';
errorEl.textContent = msg;
errorEl.style.display = 'block';
}
if (!attachmentId) {
showError('No model specified (missing ?id= parameter)');
return;
}
showFormat(detectFormat(fileName));
// Initialize the embedded viewer
// Note: v0.18.0 loads WASM (occt-import-js) from CDN automatically
const viewer = new OV.EmbeddedViewer(container, {
backgroundColor: new OV.RGBAColor(240, 242, 245, 255),
defaultColor: new OV.RGBColor(33, 150, 243),
edgeSettings: new OV.EdgeSettings(false, new OV.RGBColor(0, 0, 0), 1),
});
// Fetch the file ourselves (with session credentials) then load as blob
loadingMsg.textContent = 'Downloading ' + fileName + '...';
const modelUrl = '/fp/3d-model/' + attachmentId + '/' + encodeURIComponent(fileName);
fetch(modelUrl, { credentials: 'same-origin' })
.then(function(resp) {
if (!resp.ok) throw new Error('HTTP ' + resp.status + ': ' + resp.statusText);
return resp.arrayBuffer();
})
.then(function(buffer) {
loadingMsg.textContent = 'Parsing ' + fileName + '...';
// Create a File object so O3DV can detect format from the name
var file = new File([buffer], fileName, { type: 'application/octet-stream' });
viewer.LoadModelFromFileList([file]);
// Poll for completion
var checkCount = 0;
var checkInterval = setInterval(function() {
checkCount++;
try {
var model = viewer.GetModel();
if (model && model.MeshCount() > 0) {
loadingEl.style.display = 'none';
clearInterval(checkInterval);
}
} catch(e) {}
if (checkCount > 600) {
clearInterval(checkInterval);
if (loadingEl.style.display !== 'none') {
showError('Timeout parsing model. STEP files may take a minute on first load (WASM engine init).');
}
}
}, 100);
})
.catch(function(err) {
showError('Failed to load model: ' + err.message);
});
// ---- View preset functions (Top/Front/Side/Iso) ----
// Online3DViewer's internal viewer exposes a Camera object we can manipulate.
window.setView = function(view) {
try {
const v = viewer.GetViewer();
if (!v) return;
const camera = v.GetCamera();
if (!camera) return;
// Compute distance from current camera to keep zoom roughly consistent
const eye = camera.eye;
const center = camera.center;
const dx = eye.x - center.x, dy = eye.y - center.y, dz = eye.z - center.z;
const dist = Math.sqrt(dx*dx + dy*dy + dz*dz) || 100;
let newEye, newUp;
switch (view) {
case 'top':
newEye = new OV.Coord3D(center.x, center.y, center.z + dist);
newUp = new OV.Coord3D(0, 1, 0);
break;
case 'bottom':
newEye = new OV.Coord3D(center.x, center.y, center.z - dist);
newUp = new OV.Coord3D(0, 1, 0);
break;
case 'front':
newEye = new OV.Coord3D(center.x, center.y - dist, center.z);
newUp = new OV.Coord3D(0, 0, 1);
break;
case 'back':
newEye = new OV.Coord3D(center.x, center.y + dist, center.z);
newUp = new OV.Coord3D(0, 0, 1);
break;
case 'left':
newEye = new OV.Coord3D(center.x - dist, center.y, center.z);
newUp = new OV.Coord3D(0, 0, 1);
break;
case 'right':
newEye = new OV.Coord3D(center.x + dist, center.y, center.z);
newUp = new OV.Coord3D(0, 0, 1);
break;
case 'iso':
default:
const d = dist / Math.sqrt(3);
newEye = new OV.Coord3D(center.x + d, center.y - d, center.z + d);
newUp = new OV.Coord3D(0, 0, 1);
break;
}
const newCam = new OV.Camera(newEye, center, newUp, camera.fov);
v.SetCamera(newCam);
v.Render();
} catch(e) {
console.warn('setView failed:', e);
}
};
window.fitToView = function() {
try {
const v = viewer.GetViewer();
if (v && v.FitSphereToWindow) {
// FitSphereToWindow uses the model's bounding sphere
v.FitSphereToWindow(v.GetBoundingSphere(() => true), false);
v.Render();
}
} catch(e) {
console.warn('fitToView failed:', e);
}
};
window.takeScreenshot = function() {
try {
const v = viewer.GetViewer();
if (!v) return;
// Get the renderer's canvas and convert to PNG
const canvas = v.GetCanvas ? v.GetCanvas() : null;
if (!canvas) {
// Fallback: find canvas inside container
const c = container.querySelector('canvas');
if (!c) return;
downloadCanvas(c);
return;
}
downloadCanvas(canvas);
} catch(e) {
console.warn('screenshot failed:', e);
}
};
function downloadCanvas(canvas) {
canvas.toBlob(function(blob) {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
const stamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
a.download = (fileName.replace(/\.[^.]+$/, '') || 'model') + '-' + stamp + '.png';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, 'image/png');
}
})();
</script>
</body>
</html>