// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { // Callback for the interaction class vtkIPWCallback : public vtkCommand { public: static vtkIPWCallback* New() { return new vtkIPWCallback; } void Execute(vtkObject* caller, unsigned long, void*) override { auto* planeWidget = reinterpret_cast(caller); auto* rep = reinterpret_cast(planeWidget->GetRepresentation()); rep->GetPlane(this->plane); } vtkIPWCallback() = default; vtkPlane* plane{ nullptr }; }; vtkSmartPointer clipper; } // namespace extern "C" { EMSCRIPTEN_KEEPALIVE void AbortClip() { // set abort flag. clipper->SetAbortExecuteAndUpdateTime(); } EMSCRIPTEN_KEEPALIVE void ResetAbortFlagForClip() { // set abort flag. clipper->SetAbortExecute(false); } } //------------------------------------------------------------------------------ // Main //------------------------------------------------------------------------------ int main(int argc, char** argv) { std::array ndims; VTK_FROM_CHARS_IF_ERROR_RETURN(argv[1], ndims[0], EXIT_FAILURE); VTK_FROM_CHARS_IF_ERROR_RETURN(argv[2], ndims[1], EXIT_FAILURE); VTK_FROM_CHARS_IF_ERROR_RETURN(argv[3], ndims[2], EXIT_FAILURE); std::cout << "Generating " << ndims[0] << 'x' << ndims[1] << 'x' << ndims[2] << " block of hexahedra\n"; // Create pipeline vtkNew ugridSource; ugridSource->SetCellType(VTK_HEXAHEDRON); ugridSource->SetBlocksDimensions(ndims.data()); ugridSource->Update(); vtkNew ugridMapper; ugridMapper->SetInputConnection(ugridSource->GetOutputPort()); vtkNew ugridActor; ugridActor->SetMapper(ugridMapper); ugridActor->GetProperty()->SetOpacity(0.3); clipper = vtk::TakeSmartPointer(vtkTableBasedClipDataSet::New()); vtkNew plane; auto* ugrid = ugridSource->GetOutput(); std::array bounds; ugrid->GetBounds(bounds.data()); std::array origin; for (int i = 0; i < 3; ++i) { origin[i] = 0.5 * (bounds[i * 2] + bounds[i * 2 + 1]); } plane->SetNormal(1, 0, 0); plane->SetOrigin(origin.data()); clipper->SetClipFunction(plane); clipper->SetInputData(ugrid); // Render vtkMapper::SetResolveCoincidentTopologyToPolygonOffset(); vtkNew clippedMapper; clippedMapper->SetRelativeCoincidentTopologyPolygonOffsetParameters(1, 1); clippedMapper->SetInputConnection(clipper->GetOutputPort()); vtkNew clippedActor; clippedActor->SetMapper(clippedMapper); clippedActor->GetProperty()->SetEdgeVisibility(true); clippedActor->GetProperty()->SetEdgeColor(0, 0, 1); // Create a renderer, render window, and interactor vtkNew renderer; vtkNew renderWindow; renderWindow->SetMultiSamples(0); renderWindow->AddRenderer(renderer); vtkNew renderWindowInteractor; renderWindowInteractor->SetRenderWindow(renderWindow); vtkNew style; renderWindowInteractor->SetInteractorStyle(style); style->SetDefaultRenderer(renderer); // Add the actors to the scene renderer->AddActor(ugridActor); renderer->AddActor(clippedActor); // The callback will do the work. vtkNew myCallback; myCallback->plane = plane; vtkNew rep; rep->SetPlaceFactor(1.25); // This must be set prior to placing the widget. rep->PlaceWidget(bounds.data()); rep->SetPlane(plane); rep->SetDrawOutline(false); vtkNew planeWidget; planeWidget->SetInteractor(renderWindowInteractor); planeWidget->SetRepresentation(rep); planeWidget->AddObserver(vtkCommand::InteractionEvent, myCallback); renderer->GetActiveCamera()->Azimuth(-60); renderer->GetActiveCamera()->Elevation(30); renderer->ResetCamera(); renderer->GetActiveCamera()->Zoom(0.75); // Render and interact. renderWindowInteractor->Initialize(); renderWindow->Render(); planeWidget->On(); // clang-format off // Trigger abort when mouse move occurs followed by mouse down. // Reset the abort flag after mouse button is released so that the clipper // can execute with the new plane orientation. MAIN_THREAD_EM_ASM({ var mouseDown = false; let canvas = document.getElementById("canvas"); canvas.addEventListener('mousedown', (e) => { mouseDown = true; Module._AbortClip(); e.preventDefault(); }); canvas.addEventListener('mousemove', () => { if (mouseDown) { Module._AbortClip(); } }); canvas.addEventListener('mouseup', (e) => { mouseDown = false; Module._ResetAbortFlagForClip(); }); // Resize the canvas to fill up window space. setTimeout(() => window.dispatchEvent(new Event("resize")), 3000) }); // clang-format on // Start event loop renderWindowInteractor->Start(); return 0; }