diff --git a/visualisation/CMakeLists.txt b/visualisation/CMakeLists.txt new file mode 100644 index 00000000..5790f425 --- /dev/null +++ b/visualisation/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) + +project(GridViewer) + +list(APPEND CMAKE_PREFIX_PATH "/Users/peterboyle/QCD/vtk/VTK-9.4.2-install/") + +find_package(VTK COMPONENTS + CommonColor + CommonCore + FiltersCore + FiltersModeling + IOImage + IOFFMPEG + InteractionStyle + InteractionWidgets + RenderingContextOpenGL2 + RenderingCore + RenderingFreeType + RenderingGL2PSOpenGL2 + RenderingOpenGL2 +) + +if (NOT VTK_FOUND) + message(FATAL_ERROR "GridViewer: Unable to find the VTK build folder.") +endif() + +# Prevent a "command line is too long" failure in Windows. +set(CMAKE_NINJA_FORCE_RESPONSE_FILE "ON" CACHE BOOL "Force Ninja to use response files.") + +add_executable(FieldDensityAnimate MACOSX_BUNDLE FieldDensityAnimate.cxx ) + target_link_libraries(FieldDensityAnimate PRIVATE ${VTK_LIBRARIES} +) +# vtk_module_autoinit is needed +vtk_module_autoinit( + TARGETS FieldDensityAnimate + MODULES ${VTK_LIBRARIES} +) diff --git a/visualisation/E8_vs_Topo8.avi b/visualisation/E8_vs_Topo8.avi new file mode 100644 index 00000000..70d5c789 Binary files /dev/null and b/visualisation/E8_vs_Topo8.avi differ diff --git a/visualisation/FieldDensity.py b/visualisation/FieldDensity.py new file mode 100644 index 00000000..f63ceaf4 --- /dev/null +++ b/visualisation/FieldDensity.py @@ -0,0 +1,285 @@ +#!/usr/bin/env python + +# noinspection PyUnresolvedReferences +import math +import vtk +import vtkmodules.vtkInteractionStyle +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonColor import vtkNamedColors +from vtkmodules.vtkCommonCore import ( + VTK_VERSION_NUMBER, + vtkVersion +) +from vtkmodules.vtkCommonCore import VTK_DOUBLE +from vtkmodules.vtkCommonDataModel import vtkImageData +from vtkmodules.vtkFiltersCore import ( + vtkMarchingCubes, + vtkStripper +) +from vtkmodules.vtkFiltersModeling import vtkOutlineFilter +from vtkmodules.vtkIOImage import ( + vtkMetaImageReader, + vtkJPEGWriter, + vtkPNGWriter +) +from vtkmodules.vtkRenderingCore import ( + vtkActor, + vtkCamera, + vtkPolyDataMapper, + vtkProperty, + vtkRenderWindow, + vtkRenderWindowInteractor, + vtkRenderer, + vtkWindowToImageFilter +) + + +class vtkTimerCallback(): + def __init__(self, steps, imageData, iren): + self.timer_count = 0 + self.steps = steps + self.imageData = imageData + self.iren = iren + self.timerId = None + self.step = 0 + + def execute(self, obj, event): + + print(self.timer_count) + + dims = self.imageData.GetDimensions() + + t=self.step/10.0 + + z0 = 2 + y0 = 4 + x0 = 4 + z1 = 14 + y1 = 12 + x1 = 12 + for z in range(dims[2]): + for y in range(dims[1]): + for x in range(dims[0]): + self.imageData.SetScalarComponentFromDouble(x, y, z, 0, + math.sin(t)*math.exp(-0.25*((x-x0)*(x-x0)+(y-y0)*(y-y0)+(z-z0)*(z-z0))) + - math.cos(t)*math.exp(-0.25*((x-x1)*(x-x1)+(y-y1)*(y-y1)+(z-z1)*(z-z1)))) + + self.imageData.Modified() + + iren = obj + iren.GetRenderWindow().Render() + self.timer_count += 1 + self.step += 1 + + if self.step >= self.steps : + iren.DestroyTimer(self.timerId) + +def WriteImage(fileName, renWin): + ''' + ''' + + import os + + if fileName: + # Select the writer to use. + path, ext = os.path.splitext(fileName) + ext = ext.lower() + if not ext: + ext = '.png' + fileName = fileName + ext + elif ext == '.jpg': + writer = vtkJPEGWriter() + else: + writer = vtkPNGWriter() + + windowto_image_filter = vtkWindowToImageFilter() + windowto_image_filter.SetInput(renWin) + windowto_image_filter.SetScale(1) # image quality + windowto_image_filter.SetInputBufferTypeToRGBA() + + writer.SetFileName(fileName) + writer.SetInputConnection(windowto_image_filter.GetOutputPort()) + writer.Write() + else: + raise RuntimeError('Need a filename.') + + +def main(): + + colors = vtkNamedColors() + + file_name = get_program_parameters() + + colors.SetColor('InstantonColor', [240, 184, 160, 255]) + colors.SetColor('BackfaceColor', [255, 229, 200, 255]) + colors.SetColor('BkgColor', [51, 77, 102, 255]) + + # Create the renderer, the render window, and the interactor. The renderer + # draws into the render window, the interactor enables mouse- and + # keyboard-based interaction with the data within the render window. + # + a_renderer = vtkRenderer() + ren_win = vtkRenderWindow() + ren_win.AddRenderer(a_renderer) + + iren = vtkRenderWindowInteractor() + iren.SetRenderWindow(ren_win) + + # The following reader is used to read a series of 2D slices (images) + # that compose the volume. The slice dimensions are set, and the + # pixel spacing. The data Endianness must also be specified. The reader + # uses the FilePrefix in combination with the slice number to construct + # filenames using the format FilePrefix.%d. (In this case the FilePrefix + # is the root name of the file: quarter.) + imageData = vtkImageData() + imageData.SetDimensions(16, 16, 16) + imageData.AllocateScalars(VTK_DOUBLE, 1) + + dims = imageData.GetDimensions() + + # Fill every entry of the image data with '2.0' + # Set the input data + for z in range(dims[2]): + z0 = dims[2]/2 + for y in range(dims[1]): + y0 = dims[1]/2 + for x in range(dims[0]): + x0 = dims[0]/2 + imageData.SetScalarComponentFromDouble(x, y, z, 0, math.exp(-0.25*((x-x0)*(x-x0)+(y-y0)*(y-y0)+z*z)) - math.exp(-0.25*((x-x0)*(x-x0)+y*y+(z-z0)*(z-z0)))) + + instanton_extractor = vtkMarchingCubes() + instanton_extractor.SetInputData(imageData) + instanton_extractor.SetValue(0, 0.1) + + instanton_stripper = vtkStripper() + instanton_stripper.SetInputConnection(instanton_extractor.GetOutputPort()) + + instanton_mapper = vtkPolyDataMapper() + instanton_mapper.SetInputConnection(instanton_stripper.GetOutputPort()) + instanton_mapper.ScalarVisibilityOff() + + instanton = vtkActor() + instanton.SetMapper(instanton_mapper) + instanton.GetProperty().SetDiffuseColor(colors.GetColor3d('InstantonColor')) + instanton.GetProperty().SetSpecular(0.3) + instanton.GetProperty().SetSpecularPower(20) + instanton.GetProperty().SetOpacity(0.5) + + # The triangle stripper is used to create triangle strips from the + # isosurface these render much faster on may systems. + antiinstanton_extractor = vtkMarchingCubes() + antiinstanton_extractor.SetInputData(imageData) + antiinstanton_extractor.SetValue(0, -0.1) + + antiinstanton_stripper = vtkStripper() + antiinstanton_stripper.SetInputConnection(antiinstanton_extractor.GetOutputPort()) + + antiinstanton_mapper = vtkPolyDataMapper() + antiinstanton_mapper.SetInputConnection(antiinstanton_stripper.GetOutputPort()) + antiinstanton_mapper.ScalarVisibilityOff() + + antiinstanton = vtkActor() + antiinstanton.SetMapper(antiinstanton_mapper) + antiinstanton.GetProperty().SetDiffuseColor(colors.GetColor3d('Ivory')) + + # An outline provides box around the data. + outline_data = vtkOutlineFilter() + outline_data.SetInputData(imageData) + + map_outline = vtkPolyDataMapper() + map_outline.SetInputConnection(outline_data.GetOutputPort()) + + outline = vtkActor() + outline.SetMapper(map_outline) + outline.GetProperty().SetColor(colors.GetColor3d('Black')) + + # It is convenient to create an initial view of the data. The FocalPoint + # and Position form a vector direction. Later on (ResetCamera() method) + # this vector is used to position the camera to look at the data in + # this direction. + a_camera = vtkCamera() + a_camera.SetViewUp(0, 0, -1) + a_camera.SetPosition(0, -100, 0) + a_camera.SetFocalPoint(0, 0, 0) + a_camera.ComputeViewPlaneNormal() + a_camera.Azimuth(30.0) + a_camera.Elevation(30.0) + + # Actors are added to the renderer. An initial camera view is created. + # The Dolly() method moves the camera towards the FocalPoint, + # thereby enlarging the image. + a_renderer.AddActor(outline) + a_renderer.AddActor(instanton) + a_renderer.AddActor(antiinstanton) + a_renderer.SetActiveCamera(a_camera) + a_renderer.ResetCamera() + a_camera.Dolly(1.0) + + # Set a background color for the renderer and set the size of the + # render window (expressed in pixels). + a_renderer.SetBackground(colors.GetColor3d('BkgColor')) + ren_win.SetSize(1024, 1024) + ren_win.SetWindowName('ExpoDemo') + + # Note that when camera movement occurs (as it does in the Dolly() + # method), the clipping planes often need adjusting. Clipping planes + # consist of two planes: near and far along the view direction. The + # near plane clips out objects in front of the plane the far plane + # clips out objects behind the plane. This way only what is drawn + # between the planes is actually rendered. + a_renderer.ResetCameraClippingRange() + + # write image + # WriteImage('exp.jpg',ren_win) + + # Sign up to receive TimerEvent + cb = vtkTimerCallback(200, imageData, iren) + iren.AddObserver('TimerEvent', cb.execute) + cb.timerId = iren.CreateRepeatingTimer(50) + + # start the interaction and timer + ren_win.Render() + + # Initialize the event loop and then start it. + iren.Initialize() + iren.Start() + + +def get_program_parameters(): + import argparse + description = 'Simple lattice volumetric demo' + epilogue = ''' + Derived from VTK/Examples/Cxx/Medical2.cxx + ''' + parser = argparse.ArgumentParser(description=description, epilog=epilogue, + formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument('filename', help='FieldDensity.py') + args = parser.parse_args() + return args.filename + + +def vtk_version_ok(major, minor, build): + """ + Check the VTK version. + + :param major: Major version. + :param minor: Minor version. + :param build: Build version. + :return: True if the requested VTK version is greater or equal to the actual VTK version. + """ + needed_version = 10000000000 * int(major) + 100000000 * int(minor) + int(build) + try: + vtk_version_number = VTK_VERSION_NUMBER + except AttributeError: # as error: + ver = vtkVersion() + vtk_version_number = 10000000000 * ver.GetVTKMajorVersion() + 100000000 * ver.GetVTKMinorVersion() \ + + ver.GetVTKBuildVersion() + if vtk_version_number >= needed_version: + return True + else: + return False + + +if __name__ == '__main__': + main() diff --git a/visualisation/FieldDensityAnimate.cxx b/visualisation/FieldDensityAnimate.cxx new file mode 100644 index 00000000..825bb626 --- /dev/null +++ b/visualisation/FieldDensityAnimate.cxx @@ -0,0 +1,490 @@ +// Derived from VTK/Examples/Cxx/Medical2.cxx +// The example reads a volume dataset, extracts two isosurfaces that +// represent the skin and bone, and then displays them. +// +// Modified heavily by Peter Boyle to display lattice field theory data as movies and compare multiple files + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MPEG +#ifdef MPEG +#include +#endif + +#include +#include +#include +#include + +#include +#include + +#include + +#define USE_FLYING_EDGES +#ifdef USE_FLYING_EDGES +#include +typedef vtkFlyingEdges3D isosurface; +#else +#include +typedef vtkMarchingCubes isosurface; +#endif + +int mpeg = 0 ; +int xlate = 0 ; + +template void readFile(T& out, std::string const fname){ +#ifdef HAVE_LIME + Grid::emptyUserRecord record; + Grid::ScidacReader RD; + RD.open(fname); + RD.readScidacFieldRecord(out,record); + RD.close(); +#endif +} +using namespace Grid; + +class FrameUpdater : public vtkCallbackCommand +{ +public: + + FrameUpdater() { + TimerCount = 0; + xoff = 0; + t = 0; + imageData = nullptr; + grid_data = nullptr; + timerId = 0; + maxCount = -1; + } + + static FrameUpdater* New() + { + FrameUpdater* cb = new FrameUpdater; + cb->TimerCount = 0; + return cb; + } + + virtual void Execute(vtkObject* caller, unsigned long eventId,void* vtkNotUsed(callData)) + { + const int max=256; + char text_string[max]; + + if (this->TimerCount < this->maxCount) { + + if (vtkCommand::TimerEvent == eventId) + { + ++this->TimerCount; + + // Make a new frame + auto latt_size = grid_data->Grid()->GlobalDimensions(); + for(int xx=0;xxSetScalarComponentFromDouble(xx,yy,zz,0,value); + }}} + + if ( xlate ) { + xoff = (xoff + 1)%latt_size[0]; + if ( xoff== 0 ) t = (t+1)%latt_size[3]; + } else { + t = (t+1)%latt_size[3]; + if ( t== 0 ) xoff = (xoff + 1)%latt_size[0]; + } + + snprintf(text_string,max,"T=%d",t); + text->SetInput(text_string); + + std::cout << this->TimerCount<<"/"<Modified(); + + vtkRenderWindowInteractor* iren = dynamic_cast(caller); + iren->GetRenderWindow()->Render(); + + } + } + + if (this->TimerCount >= this->maxCount) { + vtkRenderWindowInteractor* iren = dynamic_cast(caller); + if (this->timerId > -1) + { + iren->DestroyTimer(this->timerId); + } + } + } + + +private: + int TimerCount; + int xoff; + int t; +public: + Grid::LatticeComplexD * grid_data; + vtkImageData* imageData = nullptr; + vtkTextActor* text = nullptr; + vtkFFMPEGWriter *writer = nullptr; + int timerId ; + int maxCount ; + double rms; + isosurface * posExtractor; + isosurface * negExtractor; +}; +class SliderCallback : public vtkCommand +{ +public: + static SliderCallback* New() + { + return new SliderCallback; + } + virtual void Execute(vtkObject* caller, unsigned long eventId, void* callData) + { + vtkSliderWidget *sliderWidget = vtkSliderWidget::SafeDownCast(caller); + if (sliderWidget) + { + contour = ((vtkSliderRepresentation *)sliderWidget->GetRepresentation())->GetValue(); + } + for(int i=0;iposExtractor->SetValue(0, SliderCallback::contour*fu_list[i]->rms); + fu_list[i]->negExtractor->SetValue(0, -SliderCallback::contour*fu_list[i]->rms); + fu_list[i]->posExtractor->Modified(); + fu_list[i]->negExtractor->Modified(); + } + } +public: + static double contour; + std::vector fu_list; +}; + + +double SliderCallback::contour; + +int main(int argc, char* argv[]) +{ + using namespace Grid; + + Grid_init(&argc, &argv); + GridLogLayout(); + + auto latt_size = GridDefaultLatt(); + auto simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); + auto mpi_layout = GridDefaultMpi(); + GridCartesian Grid(latt_size, simd_layout, mpi_layout); + + + std::cout << argc << " command Line arguments "< file_list; + double default_contour = 1.0; + std::string arg; +#ifdef MPEG + if( GridCmdOptionExists(argv,argv+argc,"--mpeg") ){ + mpeg = 1; + } +#endif + + if( GridCmdOptionExists(argv,argv+argc,"--xlate") ){ + xlate = 1; + } + + if( GridCmdOptionExists(argv,argv+argc,"--isosurface") ){ + arg=GridCmdOptionPayload(argv,argv+argc,"--isosurface"); + GridCmdOptionFloat(arg,default_contour); + } + if( GridCmdOptionExists(argv,argv+argc,"--file1") ){ + arg = GridCmdOptionPayload(argv,argv+argc,"--file1"); + file_list.push_back(arg); + } + if( GridCmdOptionExists(argv,argv+argc,"--file2") ){ + arg = GridCmdOptionPayload(argv,argv+argc,"--file2"); + file_list.push_back(arg); + } + if( GridCmdOptionExists(argv,argv+argc,"--file3") ){ + arg = GridCmdOptionPayload(argv,argv+argc,"--file3"); + file_list.push_back(arg); + } + if( GridCmdOptionExists(argv,argv+argc,"--file4") ){ + arg = GridCmdOptionPayload(argv,argv+argc,"--file4"); + file_list.push_back(arg); + } + for(int c=0;c colors; + std::array posColor{{240, 184, 160, 255}}; colors->SetColor("posColor", posColor.data()); + std::array bkg{{51, 77, 102, 255}}; colors->SetColor("BkgColor", bkg.data()); + + // Create the renderer, the render window, and the interactor. The renderer + // draws into the render window, the interactor enables mouse- and + // keyboard-based interaction with the data within the render window. + // + vtkNew renWin; + vtkNew iren; + iren->SetRenderWindow(renWin); + + + std::vector data(file_list.size(),&Grid); + FieldMetaData header; + + + int frameCount; + if ( mpeg ) frameCount = latt_size[3]; + else frameCount = latt_size[0] * latt_size[3]; + + std::vector fu_list; + for (int f=0;f aCamera; + aCamera->SetViewUp(0, 0, -1); + aCamera->SetPosition(0, -1000, 0); + aCamera->SetFocalPoint(0, 0, 0); + aCamera->ComputeViewPlaneNormal(); + aCamera->Azimuth(30.0); + aCamera->Elevation(30.0); + + + vtkNew aRenderer; + renWin->AddRenderer(aRenderer); + + double vol = data[f].Grid()->gSites(); + + std::cout << "Reading "< imageData; + imageData->SetDimensions(latt_size[0],latt_size[1],latt_size[2]); + imageData->AllocateScalars(VTK_DOUBLE, 1); + for(int xx=0;xxSetScalarComponentFromDouble(xx,yy,zz,0,value); + }}} + + vtkNew posExtractor; + posExtractor->SetInputData(imageData); + posExtractor->SetValue(0, contour); + + vtkNew posStripper; + posStripper->SetInputConnection(posExtractor->GetOutputPort()); + + vtkNew posMapper; + posMapper->SetInputConnection(posStripper->GetOutputPort()); + posMapper->ScalarVisibilityOff(); + + vtkNew pos; + pos->SetMapper(posMapper); + pos->GetProperty()->SetDiffuseColor(colors->GetColor3d("posColor").GetData()); + pos->GetProperty()->SetSpecular(0.3); + pos->GetProperty()->SetSpecularPower(20); + pos->GetProperty()->SetOpacity(0.5); + + // An isosurface, or contour value is set + // The triangle stripper is used to create triangle strips from the + // isosurface; these render much faster on may systems. + vtkNew negExtractor; + negExtractor->SetInputData(imageData); + negExtractor->SetValue(0, -contour); + + vtkNew negStripper; + negStripper->SetInputConnection(negExtractor->GetOutputPort()); + + vtkNew negMapper; + negMapper->SetInputConnection(negStripper->GetOutputPort()); + negMapper->ScalarVisibilityOff(); + + vtkNew neg; + neg->SetMapper(negMapper); + neg->GetProperty()->SetDiffuseColor(colors->GetColor3d("Ivory").GetData()); + + // An outline provides context around the data. + vtkNew outlineData; + outlineData->SetInputData(imageData); + + vtkNew mapOutline; + mapOutline->SetInputConnection(outlineData->GetOutputPort()); + + vtkNew outline; + outline->SetMapper(mapOutline); + outline->GetProperty()->SetColor(colors->GetColor3d("Black").GetData()); + + vtkNew Text; + Text->SetInput(file_list[f].c_str()); + Text->SetPosition2(0,0); + Text->GetTextProperty()->SetFontSize(48); + Text->GetTextProperty()->SetColor(colors->GetColor3d("Gold").GetData()); + + vtkNew TextT; + TextT->SetInput("T=0"); + TextT->SetPosition(0,.9*1025); + TextT->GetTextProperty()->SetFontSize(48); + TextT->GetTextProperty()->SetColor(colors->GetColor3d("Gold").GetData()); + + + // Actors are added to the renderer. An initial camera view is created. + // The Dolly() method moves the camera towards the FocalPoint, + // thereby enlarging the image. + aRenderer->AddActor(Text); + aRenderer->AddActor(TextT); + aRenderer->AddActor(outline); + aRenderer->AddActor(pos); + aRenderer->AddActor(neg); + + // Sign up to receive TimerEvent + vtkNew fu; + fu->imageData = imageData; + fu->grid_data = &data[f]; + fu->text = TextT; + fu->maxCount = frameCount; + fu->posExtractor = posExtractor; + fu->negExtractor = negExtractor; + fu->rms = rms; + + iren->AddObserver(vtkCommand::TimerEvent, fu); + + aRenderer->SetActiveCamera(aCamera); + aRenderer->ResetCamera(); + aRenderer->SetBackground(colors->GetColor3d("BkgColor").GetData()); + aCamera->Dolly(1.0); + + double nf = file_list.size(); + std::cout << " Adding renderer " <SetViewport((1.0/nf)*f, 0.0,(1.0/nf)*(f+1) , 1.0); + + // Note that when camera movement occurs (as it does in the Dolly() + // method), the clipping planes often need adjusting. Clipping planes + // consist of two planes: near and far along the view direction. The + // near plane clips out objects in front of the plane; the far plane + // clips out objects behind the plane. This way only what is drawn + // between the planes is actually rendered. + aRenderer->ResetCameraClippingRange(); + + fu_list.push_back(fu); + } + + + // Set a background color for the renderer and set the size of the + // render window (expressed in pixels). + // Initialize the event loop and then start it. + renWin->SetSize(1024*file_list.size(), 1024); + renWin->SetWindowName("FieldDensity"); + renWin->Render(); + + iren->Initialize(); + + if ( mpeg ) { +#ifdef MPEG + vtkWindowToImageFilter *imageFilter = vtkWindowToImageFilter::New(); + imageFilter->SetInput( renWin ); + imageFilter->SetInputBufferTypeToRGB(); + + vtkFFMPEGWriter *writer = vtkFFMPEGWriter::New(); + writer->SetFileName("movie.avi"); + writer->SetRate(1); + writer->SetInputConnection(imageFilter->GetOutputPort()); + writer->Start(); + + for(int i=0;imaxCount;i++){ + for(int f=0;fExecute(iren,vtkCommand::TimerEvent,nullptr); + } + imageFilter->Modified(); + writer->Write(); + } + writer->End(); + writer->Delete(); +#else + assert(-1 && "MPEG support not compiled"); +#endif + } else { + + // Add control of contour threshold + // Create a slider widget + vtkSmartPointer sliderRep = vtkSmartPointer::New(); + sliderRep->SetMinimumValue(0.1); + sliderRep->SetMaximumValue(5.0); + sliderRep->SetValue(1.0); + sliderRep->SetTitleText("Fraction RMS"); + // Set color properties: + + // Change the color of the knob that slides + // sliderRep->GetSliderProperty()->SetColor(colors->GetColor3d("Green").GetData()); + sliderRep->GetTitleProperty()->SetColor(colors->GetColor3d("AliceBlue").GetData()); + sliderRep->GetLabelProperty()->SetColor(colors->GetColor3d("AliceBlue").GetData()); + sliderRep->GetSelectedProperty()->SetColor(colors->GetColor3d("DeepPink").GetData()); + + // Change the color of the bar + sliderRep->GetTubeProperty()->SetColor(colors->GetColor3d("MistyRose").GetData()); + sliderRep->GetCapProperty()->SetColor(colors->GetColor3d("Yellow").GetData()); + sliderRep->SetSliderLength(0.05); + sliderRep->SetSliderWidth(0.025); + sliderRep->SetEndCapLength(0.02); + + double nf = file_list.size(); + sliderRep->GetPoint1Coordinate()->SetCoordinateSystemToNormalizedDisplay(); + sliderRep->GetPoint1Coordinate()->SetValue(0.1, 0.1); + sliderRep->GetPoint2Coordinate()->SetCoordinateSystemToNormalizedDisplay(); + sliderRep->GetPoint2Coordinate()->SetValue(0.9/nf, 0.1); + + vtkSmartPointer sliderWidget = vtkSmartPointer::New(); + sliderWidget->SetInteractor(iren); + sliderWidget->SetRepresentation(sliderRep); + sliderWidget->SetAnimationModeToAnimate(); + sliderWidget->EnabledOn(); + + // Create the slider callback + vtkSmartPointer slidercallback = vtkSmartPointer::New(); + slidercallback->fu_list = fu_list; + sliderWidget->AddObserver(vtkCommand::InteractionEvent, slidercallback); + + int timerId = iren->CreateRepeatingTimer(300); + std::cout << "timerId: " << timerId << std::endl; + + // Start the interaction and timer + iren->Start(); + } + + Grid_finalize(); + + return EXIT_SUCCESS; +} diff --git a/visualisation/README b/visualisation/README new file mode 100644 index 00000000..4e89be0e --- /dev/null +++ b/visualisation/README @@ -0,0 +1,87 @@ +======================================== +Visualisation of Grid / SciDAC format density fields using VTK (visualisation toolkit). Peter Boyle, 2025. +======================================== + +Uses: + +https://vtk.org + +Files are, for example, those produced by + Grid/HMC/ComputeWilsonFlow.cc +and + Grid/HMC/site_plaquette.cc + +======================================== +Prerequisites: +======================================== + + +1) Install ffmpeg-7.0.2 (developer install, includes headers and libraries). + MacOS note: must install ffmpeg from source -- homebrew only installs binaries. + + https://ffmpeg.org/download.html#releases + + + Note: the latest ffmpeg (7.1.1) broke software compatibility with VTK. + + +2) Build and install VTK-9.4.2, with FFMEG support enabled. + + This is particularly involved on MacOS, so documented here. + + cd VTK-9.4.2 + mkdir build + cd build + ccmake .. + + + Using cmake editor, set: + + FFMPEG_DIR /usr/local + + toggle "advanced mode" + + + CMAKE_EXE_LINKER_FLAGS to: + + -framework Foundation -framework AudioToolbox -framework CoreAudio -liconv -lm -framework AVFoundation -framework CoreVideo -framework CoreMedia -framework CoreGraphics -framework AudioToolbox -framework OpenGL -framework OpenGL -framework VideoToolbox -framework CoreImage -framework AppKit -framework CoreFoundation -framework CoreServices -lz -lbz2 -Wl,-framework,CoreFoundation -Wl,-framework,Security -L/usr/local/lib -lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil + + VTK really should make it easier to pick up the flags required for FFMPEG linkage, especially as they are very quirky on MacOS. + +======================================== +Grid: +======================================== + +3) Build and install a version of Grid + +4) Ensure "grid-config" is in your path. + +5) cd Grid/visualisation/ + libs=`grid-config --libs` + ldflags=`grid-config --ldflags` + cxxflags=`grid-config --cxxflags` + cxx=`grid-config --cxx` + + mkdir build + cd build + + LDFLAGS="$ldflags $libs " cmake .. -DCMAKE_CXX_COMPILER=$cxx -DCMAKE_CXX_FLAGS=$cxxflags + + make + +6) Invoke as: + + FieldDensityAnimate --isosurface --grid X.Y.Z.T --file1 SciDacDensityFile1 [--xlate] [--mpeg] + FieldDensityAnimate --isosurface --grid X.Y.Z.T --file1 SciDacDensityFile1 --file2 SciDacDensityFile2 [--xlate] [--mpeg] + +================================== +Extensions +================================== + +7) Direct calling from Grid ?: + + Not yet implemented, but could develop sufficient interface to write a Lattice scalar field into MPEG direct from running code. + +8) Example python code: FieldDensity.py . This is not interfaced to Grid. + + diff --git a/visualisation/Topo-vs-flowtime.avi b/visualisation/Topo-vs-flowtime.avi new file mode 100644 index 00000000..e575b222 Binary files /dev/null and b/visualisation/Topo-vs-flowtime.avi differ diff --git a/visualisation/cmake-command b/visualisation/cmake-command new file mode 100644 index 00000000..d19afbad --- /dev/null +++ b/visualisation/cmake-command @@ -0,0 +1,9 @@ +libs=`grid-config --libs` +ldflags=`grid-config --ldflags` +cxxflags=`grid-config --cxxflags` +cxx=`grid-config --cxx` + +mkdir build +cd build + +LDFLAGS="$ldflags $libs " cmake .. -DCMAKE_CXX_COMPILER=$cxx -DCMAKE_CXX_FLAGS=$cxxflags