web-viewer: check index.html and vtk.js into repo, to make viewable w/o building
This commit is contained in:
240
build/web-viewer/index.html
Normal file
240
build/web-viewer/index.html
Normal file
@@ -0,0 +1,240 @@
|
||||
<!-- heavily borrows from rendered cadquery docs: <https://cadquery.readthedocs.io/en/latest/examples.html> -->
|
||||
<!-- vtk.js = Visualization ToolKit; JS version of the same library cadquery uses during runtime -->
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<style>
|
||||
.vtk-viewer {
|
||||
width: 100%;
|
||||
height: 70%;
|
||||
border: 1px solid #bbb;
|
||||
}
|
||||
</style>
|
||||
<script src="vtk.js"></script>
|
||||
<script>
|
||||
const RENDERERS = {};
|
||||
var ID = 0;
|
||||
var rootContainer = null;
|
||||
|
||||
const renderWindow = vtk.Rendering.Core.vtkRenderWindow.newInstance();
|
||||
const openglRenderWindow = vtk.Rendering.OpenGL.vtkRenderWindow.newInstance();
|
||||
renderWindow.addView(openglRenderWindow);
|
||||
|
||||
const interact_style = vtk.Interaction.Style.vtkInteractorStyleManipulator.newInstance();
|
||||
|
||||
const manips = {
|
||||
rot: vtk.Interaction.Manipulators.vtkMouseCameraTrackballRotateManipulator.newInstance(),
|
||||
pan: vtk.Interaction.Manipulators.vtkMouseCameraTrackballPanManipulator.newInstance(),
|
||||
zoom1: vtk.Interaction.Manipulators.vtkMouseCameraTrackballZoomManipulator.newInstance(),
|
||||
zoom2: vtk.Interaction.Manipulators.vtkMouseCameraTrackballZoomManipulator.newInstance(),
|
||||
roll: vtk.Interaction.Manipulators.vtkMouseCameraTrackballRollManipulator.newInstance(),
|
||||
};
|
||||
|
||||
manips.zoom1.setControl(true);
|
||||
manips.zoom2.setButton(3);
|
||||
manips.roll.setShift(true);
|
||||
manips.pan.setButton(2);
|
||||
|
||||
for (var k in manips){{
|
||||
interact_style.addMouseManipulator(manips[k]);
|
||||
}};
|
||||
|
||||
const interactor = vtk.Rendering.Core.vtkRenderWindowInteractor.newInstance();
|
||||
interactor.setView(openglRenderWindow);
|
||||
interactor.initialize();
|
||||
interactor.setInteractorStyle(interact_style);
|
||||
|
||||
function setVtkRoot(rootContainer_) {
|
||||
rootContainer = rootContainer_;
|
||||
|
||||
rootContainer.style.position = 'fixed';
|
||||
//rootContainer.style.zIndex = -1;
|
||||
rootContainer.style.left = 0;
|
||||
rootContainer.style.top = 0;
|
||||
rootContainer.style.pointerEvents = 'none';
|
||||
rootContainer.style.width = '100%';
|
||||
rootContainer.style.height = '100%';
|
||||
|
||||
openglRenderWindow.setContainer(rootContainer);
|
||||
};
|
||||
|
||||
function updateViewPort(element, renderer) {
|
||||
const { innerHeight, innerWidth } = window;
|
||||
const { x, y, width, height } = element.getBoundingClientRect();
|
||||
const viewport = [
|
||||
x / innerWidth,
|
||||
1 - (y + height) / innerHeight,
|
||||
(x + width) / innerWidth,
|
||||
1 - y / innerHeight,
|
||||
];
|
||||
if (renderer) {
|
||||
renderer.setViewport(...viewport);
|
||||
}
|
||||
}
|
||||
|
||||
function recomputeViewports() {
|
||||
const rendererElems = document.querySelectorAll('.renderer');
|
||||
for (let i = 0; i < rendererElems.length; i++) {
|
||||
const elem = rendererElems[i];
|
||||
const { id } = elem;
|
||||
const renderer = RENDERERS[id];
|
||||
updateViewPort(elem, renderer);
|
||||
}
|
||||
renderWindow.render();
|
||||
}
|
||||
|
||||
function resize() {
|
||||
rootContainer.style.width = `${window.innerWidth}px`;
|
||||
openglRenderWindow.setSize(window.innerWidth, window.innerHeight);
|
||||
recomputeViewports();
|
||||
}
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
document.addEventListener('scroll', recomputeViewports);
|
||||
|
||||
|
||||
function enterCurrentRenderer(e) {
|
||||
interactor.bindEvents(document.body);
|
||||
interact_style.setEnabled(true);
|
||||
interactor.setCurrentRenderer(RENDERERS[e.target.id]);
|
||||
}
|
||||
|
||||
function exitCurrentRenderer(e) {
|
||||
interactor.setCurrentRenderer(null);
|
||||
interact_style.setEnabled(false);
|
||||
interactor.unbindEvents();
|
||||
}
|
||||
|
||||
function applyStyle(element) {
|
||||
element.classList.add('renderer');
|
||||
element.style.width = '100%';
|
||||
element.style.height = '100%';
|
||||
element.style.display = 'inline-block';
|
||||
element.style.boxSizing = 'border';
|
||||
return element;
|
||||
}
|
||||
|
||||
window.addEventListener('load', resize);
|
||||
|
||||
function render(data, parent_element, ratio){
|
||||
|
||||
// Initial setup
|
||||
const renderer = vtk.Rendering.Core.vtkRenderer.newInstance({ background: [1, 1, 1 ] });
|
||||
|
||||
// iterate over all children children
|
||||
for (var el of data){
|
||||
var trans = el.position;
|
||||
var rot = el.orientation;
|
||||
var rgba = el.color;
|
||||
var shape = el.shape;
|
||||
|
||||
// load the inline data
|
||||
var reader = vtk.IO.XML.vtkXMLPolyDataReader.newInstance();
|
||||
const textEncoder = new TextEncoder();
|
||||
reader.parseAsArrayBuffer(textEncoder.encode(shape));
|
||||
|
||||
// setup actor,mapper and add
|
||||
const mapper = vtk.Rendering.Core.vtkMapper.newInstance();
|
||||
mapper.setInputConnection(reader.getOutputPort());
|
||||
mapper.setResolveCoincidentTopologyToPolygonOffset();
|
||||
mapper.setResolveCoincidentTopologyPolygonOffsetParameters(0.5,100);
|
||||
|
||||
const actor = vtk.Rendering.Core.vtkActor.newInstance();
|
||||
actor.setMapper(mapper);
|
||||
|
||||
// set color and position
|
||||
actor.getProperty().setColor(rgba.slice(0,3));
|
||||
actor.getProperty().setOpacity(rgba[3]);
|
||||
|
||||
actor.rotateZ(rot[2]*180/Math.PI);
|
||||
actor.rotateY(rot[1]*180/Math.PI);
|
||||
actor.rotateX(rot[0]*180/Math.PI);
|
||||
|
||||
actor.setPosition(trans);
|
||||
|
||||
renderer.addActor(actor);
|
||||
};
|
||||
|
||||
//add the container
|
||||
const container = applyStyle(document.createElement("div"));
|
||||
parent_element.appendChild(container);
|
||||
container.addEventListener('mouseenter', enterCurrentRenderer);
|
||||
container.addEventListener('mouseleave', exitCurrentRenderer);
|
||||
container.id = ID;
|
||||
|
||||
renderWindow.addRenderer(renderer);
|
||||
updateViewPort(container, renderer);
|
||||
renderer.getActiveCamera().set({ position: [1, -1, 1], viewUp: [0, 0, 1] });
|
||||
renderer.resetCamera();
|
||||
|
||||
RENDERERS[ID] = renderer;
|
||||
ID++;
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- load model data. populates a global variable whose name matches the filename -->
|
||||
<script src="pinephone_case.vtk.js"></script>
|
||||
<script src="pinephone_phone.vtk.js"></script>
|
||||
|
||||
<script>
|
||||
function renderToConsole(modelName) {
|
||||
// turns out there's just one canvas for the whole renderer,
|
||||
// so this ignores the modelName and exports the whole canvas
|
||||
// var viewerCanvas = document.querySelector(`#vtk-viewer-${modelName} > canvas`);
|
||||
var viewerCanvas = document.querySelector("#vtk-viewer-root > canvas");
|
||||
console.log("logging viewer image to console to allow the Makefile to capture a static image for docs...");
|
||||
var image = viewerCanvas.toDataURL("image/png")
|
||||
console.log("vtk-viewer-canvas: pinephone_case: " + image);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="vtk-viewer-root">
|
||||
<script>
|
||||
setVtkRoot(document.currentScript.parentNode);
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div class="vtk-viewer" id="vtk-viewer-pinephone_case">
|
||||
<script>
|
||||
var parent_element = document.currentScript.parentNode;
|
||||
|
||||
var pinephone_case_options = [{
|
||||
"color": [ 1.0, 0.8, 0.0, 1.0 ],
|
||||
"position": [ 0.0, 0.0, 0.0 ],
|
||||
"orientation": [ 0.0, 0.0, 0.0 ],
|
||||
"shape": pinephone_case
|
||||
}];
|
||||
render(pinephone_case_options, parent_element);
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div class="vtk-viewer">
|
||||
<script>
|
||||
var parent_element = document.currentScript.parentNode;
|
||||
|
||||
var pinephone_case_with_phone_options = [
|
||||
{
|
||||
"color": [ 1.0, 0.8, 0.0, 1.0 ],
|
||||
"position": [ 0.0, 0.0, 0.0 ],
|
||||
"orientation": [ 0.0, 0.0, 0.0 ],
|
||||
"shape": pinephone_case
|
||||
},
|
||||
{
|
||||
"color": [ 0.2, 0.2, 0.2, 1.0 ],
|
||||
"position": [ 0.0, 0.0, 0.0 ],
|
||||
"orientation": [ 0.0, 0.0, 0.0 ],
|
||||
"shape": pinephone_phone
|
||||
}
|
||||
];
|
||||
render(pinephone_case_with_phone_options, parent_element);
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
resize();
|
||||
renderToConsole("pinephone_case");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
3
build/web-viewer/vtk.js
Normal file
3
build/web-viewer/vtk.js
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user