Files
PozzettiAndrea-ComfyUI-Geom…/web/viewer_vtk.html
T
2025-11-14 21:41:06 +01:00

360 lines
14 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>GeomPack VTK.js Viewer</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #2a2a2a;
font-family: monospace;
display: flex;
flex-direction: column;
height: 100vh;
}
#viewerContainer {
flex: 1;
position: relative;
min-height: 0;
}
#container {
width: 100%;
height: 100%;
position: relative;
}
#loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-size: 14px;
text-align: center;
z-index: 1000;
pointer-events: none;
}
#controls {
background: #1a1a1a;
border-top: 1px solid #444;
padding: 8px 12px;
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
font-size: 12px;
color: #ccc;
}
#controls label {
display: flex;
align-items: center;
gap: 4px;
}
#controls select, #controls button {
background: #333;
color: #ccc;
border: 1px solid #555;
padding: 4px 8px;
border-radius: 3px;
font-size: 12px;
font-family: monospace;
}
#controls button {
cursor: pointer;
}
#controls button:hover {
background: #444;
}
#controls input[type="checkbox"] {
cursor: pointer;
}
</style>
</head>
<body>
<div id="viewerContainer">
<div id="container"></div>
<div id="loading">Ready to load mesh</div>
</div>
<div id="controls">
<label>
<input type="checkbox" id="showEdges"> Edges
</label>
<label>
Mode:
<select id="representation">
<option value="2">Surface</option>
<option value="1">Wireframe</option>
<option value="0">Points</option>
</select>
</label>
<label id="fieldLabel" style="display:none;">
Field:
<select id="fieldSelector"></select>
</label>
<button id="resetCamera">Reset Camera</button>
<button id="screenshot">Screenshot</button>
</div>
<script type="importmap">
{
"imports": {
"@kitware/vtk.js/": "https://cdn.skypack.dev/@kitware/vtk.js@29.5.0/"
}
}
</script>
<script type="module">
// CRITICAL: Import rendering profile to load WebGL backend
import '@kitware/vtk.js/Rendering/Profiles/Geometry';
import vtkFullScreenRenderWindow from '@kitware/vtk.js/Rendering/Misc/FullScreenRenderWindow';
import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';
import vtkSTLReader from '@kitware/vtk.js/IO/Geometry/STLReader';
import vtkOBJReader from '@kitware/vtk.js/IO/Misc/OBJReader';
import vtkPolyDataNormals from '@kitware/vtk.js/Filters/Core/PolyDataNormals';
import vtkInteractorStyleTrackballCamera from '@kitware/vtk.js/Interaction/Style/InteractorStyleTrackballCamera';
console.log('[GeomPack VTK Viewer] Initializing...');
const loading = document.getElementById('loading');
const container = document.getElementById('container');
// Create full screen renderer
const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({
background: [0.16, 0.16, 0.16], // #2a2a2a
container: container,
});
const renderer = fullScreenRenderer.getRenderer();
const renderWindow = fullScreenRenderer.getRenderWindow();
const interactor = renderWindow.getInteractor();
// Set up trackball camera controls
const interactorStyle = vtkInteractorStyleTrackballCamera.newInstance();
interactor.setInteractorStyle(interactorStyle);
// Create actor and mapper for mesh
const actor = vtkActor.newInstance();
const mapper = vtkMapper.newInstance();
actor.setMapper(mapper);
// Configure appearance with better lighting properties
const property = actor.getProperty();
property.setEdgeVisibility(false);
property.setColor(1.0, 1.0, 1.0); // Bright white for visibility
property.setAmbient(0.4); // Increase ambient
property.setDiffuse(0.8); // Increase diffuse
property.setSpecular(0.3);
property.setSpecularPower(20);
property.setRepresentation(2); // 2 = surface rendering
// Add actor to scene
renderer.addActor(actor);
console.log('[GeomPack VTK Viewer DEBUG] Actor configured with lighting properties');
console.log('[GeomPack VTK Viewer DEBUG] Actor visible:', actor.getVisibility());
console.log('[GeomPack VTK Viewer DEBUG] Renderer background:', renderer.getBackground());
// Current reader
let currentReader = null;
// Load mesh function
async function loadMesh(filepath) {
console.log('[GeomPack VTK Viewer] Loading:', filepath);
console.log('[GeomPack VTK Viewer DEBUG] Full filepath URL:', filepath);
loading.textContent = 'Loading mesh...';
loading.style.display = 'block';
try {
// Determine file type
const isSTL = filepath.includes('.stl');
const isOBJ = filepath.includes('.obj');
const isGLB = filepath.includes('.glb') || filepath.includes('.gltf');
console.log('[GeomPack VTK Viewer DEBUG] File type detection - STL:', isSTL, 'OBJ:', isOBJ, 'GLB:', isGLB);
// Create appropriate reader
if (isSTL) {
console.log('[GeomPack VTK Viewer] File type: STL');
currentReader = vtkSTLReader.newInstance();
} else if (isOBJ) {
console.log('[GeomPack VTK Viewer] File type: OBJ');
currentReader = vtkOBJReader.newInstance();
} else {
const errorMsg = `Unsupported file format: ${filepath}. VTK viewer only supports STL and OBJ formats.`;
console.error('[GeomPack VTK Viewer DEBUG]', errorMsg);
throw new Error(errorMsg);
}
console.log('[GeomPack VTK Viewer DEBUG] About to fetch:', filepath);
// Fetch the file
const response = await fetch(filepath);
console.log('[GeomPack VTK Viewer DEBUG] Fetch response:', response);
console.log('[GeomPack VTK Viewer DEBUG] Response status:', response.status, response.statusText);
console.log('[GeomPack VTK Viewer DEBUG] Response OK:', response.ok);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
// Get file data
const arrayBuffer = await response.arrayBuffer();
console.log('[GeomPack VTK Viewer DEBUG] ArrayBuffer size:', arrayBuffer.byteLength);
// Parse with appropriate reader
if (isSTL) {
console.log('[GeomPack VTK Viewer DEBUG] Parsing STL...');
currentReader.parseAsArrayBuffer(arrayBuffer);
} else if (isOBJ) {
console.log('[GeomPack VTK Viewer DEBUG] Parsing OBJ...');
const text = new TextDecoder().decode(arrayBuffer);
currentReader.parseAsText(text);
}
// Get output data for logging
const polydata = currentReader.getOutputData();
console.log('[GeomPack VTK Viewer DEBUG] Polydata:', polydata);
if (!polydata) {
throw new Error('Failed to parse mesh data');
}
const numPoints = polydata.getNumberOfPoints();
const numPolys = polydata.getNumberOfPolys();
console.log('[GeomPack VTK Viewer] Loaded mesh:', numPoints, 'points,', numPolys, 'polygons');
// Check mesh bounds
const bounds = polydata.getBounds();
console.log('[GeomPack VTK Viewer DEBUG] Mesh bounds:', bounds);
// CRITICAL: Use proper VTK pipeline with normals filter
console.log('[GeomPack VTK Viewer DEBUG] Setting up VTK pipeline...');
// Create normals filter (required for STL rendering)
const normalsFilter = vtkPolyDataNormals.newInstance();
// Connect pipeline: reader -> normals -> mapper
normalsFilter.setInputConnection(currentReader.getOutputPort());
mapper.setInputConnection(normalsFilter.getOutputPort());
console.log('[GeomPack VTK Viewer DEBUG] Pipeline connected: reader -> normals -> mapper');
// Force pipeline update
mapper.update();
console.log('[GeomPack VTK Viewer DEBUG] Mapper updated');
console.log('[GeomPack VTK Viewer DEBUG] Mapper has input:', mapper.getInputData());
console.log('[GeomPack VTK Viewer DEBUG] Actor mapper:', actor.getMapper());
console.log('[GeomPack VTK Viewer DEBUG] Resetting camera...');
// Reset camera to fit mesh
renderer.resetCamera();
// Get camera and adjust view angle
const camera = renderer.getActiveCamera();
const center = [
(bounds[0] + bounds[1]) / 2,
(bounds[2] + bounds[3]) / 2,
(bounds[4] + bounds[5]) / 2
];
const maxDim = Math.max(
bounds[1] - bounds[0],
bounds[3] - bounds[2],
bounds[5] - bounds[4]
);
// Position camera at an angle for better 3D view
const distance = maxDim * 2.5;
camera.setPosition(
center[0] + distance,
center[1] + distance,
center[2] + distance
);
camera.setFocalPoint(center[0], center[1], center[2]);
camera.setViewUp(0, 0, 1);
console.log('[GeomPack VTK Viewer DEBUG] Camera position:', camera.getPosition());
console.log('[GeomPack VTK Viewer DEBUG] Camera focal point:', camera.getFocalPoint());
console.log('[GeomPack VTK Viewer DEBUG] Mesh center:', center);
console.log('[GeomPack VTK Viewer DEBUG] Max dimension:', maxDim);
console.log('[GeomPack VTK Viewer DEBUG] Distance:', distance);
// Render the scene
renderWindow.render();
console.log('[GeomPack VTK Viewer DEBUG] Initial render called');
// Force a resize to ensure proper display in iframe
setTimeout(() => {
fullScreenRenderer.resize();
renderWindow.render();
console.log('[GeomPack VTK Viewer DEBUG] Post-load resize and render');
}, 100);
console.log('[GeomPack VTK Viewer DEBUG] Rendering complete');
loading.style.display = 'none';
console.log('[GeomPack VTK Viewer] Mesh loaded successfully');
} catch (error) {
console.error('[GeomPack VTK Viewer] Error loading mesh:', error);
console.error('[GeomPack VTK Viewer DEBUG] Error type:', error?.constructor?.name);
console.error('[GeomPack VTK Viewer DEBUG] Error message:', error?.message);
console.error('[GeomPack VTK Viewer DEBUG] Error stack:', error?.stack);
loading.textContent = `Error loading mesh: ${error.message}`;
loading.style.color = '#ff6b6b';
}
}
// Listen for messages from parent window
window.addEventListener('message', (event) => {
console.log('[GeomPack VTK Viewer DEBUG] Received postMessage:', event.data);
console.log('[GeomPack VTK Viewer DEBUG] Message type:', event.data?.type);
console.log('[GeomPack VTK Viewer DEBUG] Message filepath:', event.data?.filepath);
if (event.data.type === 'LOAD_MESH') {
console.log('[GeomPack VTK Viewer] Received LOAD_MESH message:', event.data.filepath);
loadMesh(event.data.filepath);
} else {
console.warn('[GeomPack VTK Viewer DEBUG] Unknown message type or missing type');
}
});
// Handle window resize
window.addEventListener('resize', () => {
fullScreenRenderer.resize();
renderWindow.render();
});
// Control event handlers
document.getElementById('showEdges').addEventListener('change', (e) => {
property.setEdgeVisibility(e.target.checked);
renderWindow.render();
});
document.getElementById('representation').addEventListener('change', (e) => {
property.setRepresentation(parseInt(e.target.value));
renderWindow.render();
});
document.getElementById('resetCamera').addEventListener('click', () => {
renderer.resetCamera();
renderWindow.render();
});
document.getElementById('screenshot').addEventListener('click', async () => {
const image = await renderWindow.captureImages()[0];
// Send screenshot back to parent window
window.parent.postMessage({
type: 'SCREENSHOT',
image: image, // base64 PNG data URL
timestamp: Date.now()
}, '*');
console.log('[GeomPack VTK Viewer] Screenshot captured and sent to parent');
});
console.log('[GeomPack VTK Viewer] Ready');
console.log('[GeomPack VTK Viewer DEBUG] Waiting for LOAD_MESH postMessage...');
</script>
</body>
</html>