From af43b067a00cd66cc1188c9d5a14d795c44d235f Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Fri, 10 Apr 2026 11:23:25 -0400 Subject: [PATCH] New CLAUDE controllable visualiser --- visualisation/ControlledVisualise5D.cxx | 688 ++++++++++++++++++++++++ 1 file changed, 688 insertions(+) create mode 100644 visualisation/ControlledVisualise5D.cxx diff --git a/visualisation/ControlledVisualise5D.cxx b/visualisation/ControlledVisualise5D.cxx new file mode 100644 index 00000000..c1890917 --- /dev/null +++ b/visualisation/ControlledVisualise5D.cxx @@ -0,0 +1,688 @@ +// ControlledVisualise5D.cxx +// Derived from Visualise5D.cxx by Peter Boyle +// +// A minimal-protocol rendering engine for 5D DWF eigenvector-density data. +// Intended to be driven by an external intelligent controller (e.g. Claude) +// that handles all natural-language interpretation and state tracking. +// +// Commands are sent one per line to the named pipe /tmp/visualise_cmd. +// State is reported to stdout after every command. +// +// Wire protocol (all fields whitespace-separated): +// +// slice set Slice[dim] = N (0-based, wraps to lattice size) +// slice + increment Slice[dim] by N +// slice - decrement Slice[dim] by N +// zoom camera Dolly by factor (>1 = in, <1 = out) +// iso set isosurface threshold to x RMS +// file jump to file by absolute index +// file + advance N files +// file - go back N files +// render force a render with current state +// status print current state to stdout +// quit exit cleanly +// +// Dimension indices for 5D DWF grid (e.g. --grid 48.32.32.32.32): +// s=0 (Ls) x=1 y=2 z=3 t=4 +// For a 4D grid (--grid 32.32.32.32): +// x=0 y=1 z=2 t=3 + +#include +#include +#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 +#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 + +#define CMD_PIPE "/tmp/visualise_cmd" + +static int g_mpeg = 0; +static int g_framerate = 10; + +// ─── Thread-safe command queue ──────────────────────────────────────────────── +static std::queue g_cmdQueue; +static std::mutex g_cmdMutex; +static std::atomic g_running{true}; +static double g_spinDeg = 0.0; // degrees per poll tick; 0 = stopped + +// ─── Grid I/O ───────────────────────────────────────────────────────────────── +template +void readFile(T& out, const std::string& fname) +{ + Grid::emptyUserRecord record; + Grid::ScidacReader RD; + RD.open(fname); + RD.readScidacFieldRecord(out, record); + RD.close(); +} + +using namespace Grid; + +// ─── Command reader thread ──────────────────────────────────────────────────── +void CommandReaderThread() +{ + mkfifo(CMD_PIPE, 0666); + std::cout << "[cmd] Listening on " << CMD_PIPE << std::endl; + + while (g_running) { + int fd = open(CMD_PIPE, O_RDONLY | O_NONBLOCK); + if (fd < 0) { usleep(200000); continue; } + + int flags = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); + + char buf[4096]; + std::string partial; + ssize_t n; + while (g_running && (n = read(fd, buf, sizeof(buf) - 1)) > 0) { + buf[n] = '\0'; + partial += buf; + size_t pos; + while ((pos = partial.find('\n')) != std::string::npos) { + std::string line = partial.substr(0, pos); + if (!line.empty() && line.back() == '\r') line.pop_back(); + if (!line.empty()) { + std::lock_guard lk(g_cmdMutex); + g_cmdQueue.push(line); + } + partial = partial.substr(pos + 1); + } + } + close(fd); + } +} + +// ─── FrameUpdater ───────────────────────────────────────────────────────────── +class FrameUpdater : public vtkCallbackCommand +{ +public: + FrameUpdater() : ffile(0), TimerCount(0), old_file(-1), timerId(-2), maxCount(-1) {} + + static FrameUpdater* New() { return new FrameUpdater; } + + int ffile; + int old_file; + int timerId; + int maxCount; + + Coordinate latt; + Coordinate xyz_dims, xyz_ranges, g_xyz_ranges; + uint64_t xyz_vol; + Coordinate loop_dims, loop_ranges; + uint64_t loop_vol; + Coordinate sum_dims, sum_ranges; + uint64_t sum_vol; + Coordinate slice_dims; + Coordinate Slice; + + std::vector files; + int Nd; + GridBase* grid; + Grid::LatticeComplexD* grid_data; + + double rms; + + vtkImageData* imageData = nullptr; + vtkTextActor* text = nullptr; + isosurface* posExtractor = nullptr; + isosurface* negExtractor = nullptr; + + void SetGrid(GridBase* _grid) + { + grid = _grid; + Nd = grid->Nd(); + latt = grid->GlobalDimensions(); + grid_data = new Grid::LatticeComplexD(grid); + } + + void SetFiles(std::vector list) { files = list; old_file = -1; } + void SetSlice(Coordinate _Slice) { Slice = _Slice; } + + void SetSumDimensions(Coordinate _SumDims) + { + sum_dims = _SumDims; sum_ranges = Coordinate(Nd); sum_vol = 1; + for (int d = 0; d < Nd; d++) { sum_ranges[d] = sum_dims[d] ? latt[d] : 1; sum_vol *= sum_ranges[d]; } + } + + void SetLoopDimensions(Coordinate _LoopDims) + { + loop_dims = _LoopDims; loop_ranges = Coordinate(Nd); loop_vol = 1; + for (int d = 0; d < Nd; d++) { loop_ranges[d] = loop_dims[d] ? latt[d] : 1; loop_vol *= loop_ranges[d]; } + } + + void SetDisplayDimensions(Coordinate _xyz_dims) + { + xyz_dims = _xyz_dims; g_xyz_ranges = Coordinate(Nd); xyz_ranges = Coordinate(3); xyz_vol = 1; + for (int d = 0; d < 3; d++) { xyz_ranges[d] = latt[xyz_dims[d]]; xyz_vol *= xyz_ranges[d]; } + for (int d = 0; d < Nd; d++) { + g_xyz_ranges[d] = 1; + for (int dd = 0; dd < 3; dd++) if (xyz_dims[dd] == d) g_xyz_ranges[d] = latt[d]; + } + } + + void SetSliceDimensions() + { + Coordinate sd; + for (int d = 0; d < Nd; d++) { + if (g_xyz_ranges[d] > 1 || loop_dims[d] || sum_dims[d]) continue; + sd.push_back(d); + } + slice_dims = sd; + std::cout << " Slice dimensions: " << slice_dims << std::endl; + } + + void FillImageData(int loop_idx) + { + Coordinate loop_coor; + Lexicographic::CoorFromIndex(loop_coor, loop_idx, loop_ranges); + Coordinate xyz_coor(3), g_xyz_coor(Nd), sum_coor(Nd); + for (uint64_t xyz = 0; xyz < xyz_vol; xyz++) { + Lexicographic::CoorFromIndex(xyz_coor, xyz, xyz_ranges); + Lexicographic::CoorFromIndex(g_xyz_coor, xyz, g_xyz_ranges); + RealD val = 0.0; + for (uint64_t si = 0; si < sum_vol; si++) { + Lexicographic::CoorFromIndex(sum_coor, si, sum_ranges); + Coordinate site(Nd); + for (int d = 0; d < Nd; d++) + site[d] = (sum_coor[d] + loop_coor[d] + g_xyz_coor[d] + Slice[d]) % latt[d]; + val += real(peekSite(*grid_data, site)); + } + imageData->SetScalarComponentFromDouble(xyz_coor[0], xyz_coor[1], xyz_coor[2], 0, val); + } + imageData->Modified(); + } + + // Reload if needed, fill image, update label, render — no timer advance. + void ForceRender(vtkRenderWindowInteractor* iren) + { + int file = ((TimerCount / (int)loop_vol) + ffile) % (int)files.size(); + if (file != old_file) { + std::cout << "[render] Loading " << files[file] << std::endl; + readFile(*grid_data, files[file]); + old_file = file; + } + FillImageData(TimerCount % (int)loop_vol); + UpdateLabel(file, TimerCount % (int)loop_vol); + iren->GetRenderWindow()->Render(); + } + + virtual void Execute(vtkObject* caller, unsigned long eventId, void* callData) + { + if (vtkCommand::KeyPressEvent == eventId) { + vtkRenderWindowInteractor* iren = static_cast(caller); + std::string key = iren->GetKeySym(); + if (slice_dims.size() > 0) { + int vert = slice_dims[slice_dims.size() - 1]; + int horz = slice_dims[0]; + if (key == "Up") Slice[vert] = (Slice[vert] + 1) % latt[vert]; + if (key == "Down") Slice[vert] = (Slice[vert] + latt[vert] - 1) % latt[vert]; + if (key == "Right") Slice[horz] = (Slice[horz] + 1) % latt[horz]; + if (key == "Left") Slice[horz] = (Slice[horz] + latt[horz] - 1) % latt[horz]; + } + if (key == "greater") ffile = (ffile + 1) % (int)files.size(); + if (key == "less") ffile = (ffile - 1 + (int)files.size()) % (int)files.size(); + ForceRender(iren); + return; + } + + if (vtkCommand::TimerEvent == eventId) { + // timerId == -2: no animation timer (--notime), ignore all timer events + if (timerId < 0) return; + int tid = *(reinterpret_cast(callData)); + if (tid != timerId) return; + int file = ((TimerCount / (int)loop_vol) + ffile) % (int)files.size(); + if (file != old_file) { readFile(*grid_data, files[file]); old_file = file; } + FillImageData(TimerCount % (int)loop_vol); + UpdateLabel(file, TimerCount % (int)loop_vol); + dynamic_cast(caller)->GetRenderWindow()->Render(); + ++TimerCount; + if (TimerCount >= maxCount && timerId > -1) + dynamic_cast(caller)->DestroyTimer(timerId); + } + } + +private: + int TimerCount; + + void UpdateLabel(int file, int loop_idx) + { + Coordinate loop_coor; + Lexicographic::CoorFromIndex(loop_coor, loop_idx, loop_ranges); + // Extract tau value from filename (last '_'-delimited field) + const std::string& path = files[file]; + std::string tau = path.substr(path.rfind('_') + 1); + std::stringstream ss; + ss << "tau = " << tau << "\nSlice " << Slice << "\nLoop " << loop_coor; + text->SetInput(ss.str().c_str()); + } +}; + +// ─── SliderCallback ─────────────────────────────────────────────────────────── +class SliderCallback : public vtkCommand +{ +public: + static SliderCallback* New() { return new SliderCallback; } + virtual void Execute(vtkObject* caller, unsigned long, void*) + { + vtkSliderWidget* sw = vtkSliderWidget::SafeDownCast(caller); + if (sw) contour = ((vtkSliderRepresentation*)sw->GetRepresentation())->GetValue(); + fu->posExtractor->SetValue(0, contour * fu->rms); + fu->negExtractor->SetValue(0, -contour * fu->rms); + fu->posExtractor->Modified(); + fu->negExtractor->Modified(); + } + static double contour; + FrameUpdater* fu; +}; +double SliderCallback::contour = 1.0; + +// ─── CommandHandler ─────────────────────────────────────────────────────────── +// Minimal parser for the wire protocol. Natural-language interpretation +// is handled externally (by Claude) before commands reach this program. +class CommandHandler : public vtkCallbackCommand +{ +public: + static CommandHandler* New() { return new CommandHandler; } + + FrameUpdater* fu; + vtkCamera* camera; + vtkRenderer* renderer; + vtkRenderWindowInteractor* iren; + vtkSliderRepresentation2D* sliderRep; + int pollTimerId = -1; + double isosurfaceLevel = 1.0; // in RMS units + + void SetIsosurface(double level) + { + isosurfaceLevel = std::max(0.0, std::min(10.0, level)); + sliderRep->SetValue(isosurfaceLevel); + SliderCallback::contour = isosurfaceLevel; + fu->posExtractor->SetValue(0, isosurfaceLevel * fu->rms); + fu->negExtractor->SetValue(0, -isosurfaceLevel * fu->rms); + fu->posExtractor->Modified(); + fu->negExtractor->Modified(); + } + + void PrintStatus() + { + std::cout << "[status] file = " << fu->ffile + << " : " << fu->files[fu->ffile] << "\n" + << "[status] Slice = " << fu->Slice << "\n" + << "[status] latt = " << fu->latt << "\n" + << "[status] isosurface = " << isosurfaceLevel + << " x RMS (" << isosurfaceLevel * fu->rms << ")" << std::endl; + } + + // Execute one line of the wire protocol. + void RunLine(const std::string& line) + { + std::istringstream iss(line); + std::string verb; + if (!(iss >> verb)) return; + + // ── slice ──────────────────────────────────────────── + if (verb == "slice") { + int dim; std::string valstr; + if (!(iss >> dim >> valstr)) { std::cout << "[cmd] slice: expected dim value" << std::endl; return; } + if (dim < 0 || dim >= fu->Nd) { std::cout << "[cmd] slice: dim out of range" << std::endl; return; } + int n = (int)fu->latt[dim]; + int newval; + if (!valstr.empty() && (valstr[0] == '+' || valstr[0] == '-')) { + int delta = std::stoi(valstr); + newval = ((fu->Slice[dim] + delta) % n + n) % n; + } else { + newval = ((std::stoi(valstr) % n) + n) % n; + } + fu->Slice[dim] = newval; + fu->ForceRender(iren); + PrintStatus(); + } + + // ── zoom ──────────────────────────────────────────────────── + else if (verb == "zoom") { + double factor; + if (!(iss >> factor)) { std::cout << "[cmd] zoom: expected factor" << std::endl; return; } + camera->Dolly(factor); + renderer->ResetCameraClippingRange(); + iren->GetRenderWindow()->Render(); + } + + // ── azimuth ──────────────────────────────────────────────── + else if (verb == "azimuth") { + double deg; + if (!(iss >> deg)) { std::cout << "[cmd] azimuth: expected degrees" << std::endl; return; } + camera->Azimuth(deg); + renderer->ResetCameraClippingRange(); + iren->GetRenderWindow()->Render(); + } + + // ── elevation ────────────────────────────────────────────── + else if (verb == "elevation") { + double deg; + if (!(iss >> deg)) { std::cout << "[cmd] elevation: expected degrees" << std::endl; return; } + camera->Elevation(deg); + renderer->ResetCameraClippingRange(); + iren->GetRenderWindow()->Render(); + } + + // ── spin ────────────────────────────────────────── + // Applies azimuth rotation on every 100ms poll tick. spin 0 stops. + else if (verb == "spin") { + double deg; + if (!(iss >> deg)) { std::cout << "[cmd] spin: expected degrees" << std::endl; return; } + g_spinDeg = deg; + std::cout << "[cmd] spin rate = " << g_spinDeg << " deg/tick" << std::endl; + } + + // ── iso ────────────────────────────────────────────────────── + else if (verb == "iso") { + double val; + if (!(iss >> val)) { std::cout << "[cmd] iso: expected value" << std::endl; return; } + SetIsosurface(val); + fu->ForceRender(iren); + PrintStatus(); + } + + // ── file ─────────────────────────────────────────────── + else if (verb == "file") { + std::string valstr; + if (!(iss >> valstr)) { std::cout << "[cmd] file: expected index" << std::endl; return; } + int n = (int)fu->files.size(); + int newval; + if (!valstr.empty() && (valstr[0] == '+' || valstr[0] == '-')) { + int delta = std::stoi(valstr); + newval = ((fu->ffile + delta) % n + n) % n; + } else { + newval = ((std::stoi(valstr) % n) + n) % n; + } + fu->ffile = newval; + fu->old_file = -1; + fu->ForceRender(iren); + PrintStatus(); + } + + // ── render ─────────────────────────────────────────────────────────── + else if (verb == "render") { + fu->ForceRender(iren); + } + + // ── status ─────────────────────────────────────────────────────────── + else if (verb == "status") { + PrintStatus(); + } + + // ── quit ───────────────────────────────────────────────────────────── + else if (verb == "quit" || verb == "exit") { + g_running = false; + iren->TerminateApp(); + } + + else { + std::cout << "[cmd] Unknown command: '" << line << "'" << std::endl; + } + } + + virtual void Execute(vtkObject*, unsigned long eventId, void* callData) + { + if (eventId != vtkCommand::TimerEvent) return; + if (pollTimerId >= 0) { + int tid = *(reinterpret_cast(callData)); + if (tid != pollTimerId) return; + } + + std::vector pending; + { + std::lock_guard lk(g_cmdMutex); + while (!g_cmdQueue.empty()) { pending.push_back(g_cmdQueue.front()); g_cmdQueue.pop(); } + } + for (const auto& line : pending) { + std::cout << "[cmd] >> " << line << std::endl; + RunLine(line); + } + + // Apply continuous spin (if active) at poll-timer rate + if (g_spinDeg != 0.0) { + camera->Azimuth(g_spinDeg); + renderer->ResetCameraClippingRange(); + iren->GetRenderWindow()->Render(); + } + } +}; + +// ─── main ───────────────────────────────────────────────────────────────────── +int main(int argc, char* argv[]) +{ + using namespace Grid; + + Grid_init(&argc, &argv); + GridLogLayout(); + + auto latt_size = GridDefaultLatt(); + auto simd_layout = GridDefaultSimd(latt_size.size(), vComplex::Nsimd()); + auto mpi_layout = GridDefaultMpi(); + GridCartesian Grid(latt_size, simd_layout, mpi_layout); + + double default_contour = 1.0; + std::string arg; + + std::vector file_list({"file1","file2","file3","file4", + "file5","file6","file7","file8"}); + + if (GridCmdOptionExists(argv, argv+argc, "--files")) { + arg = GridCmdOptionPayload(argv, argv+argc, "--files"); + GridCmdOptionCSL(arg, file_list); + } +#ifdef MPEG + if (GridCmdOptionExists(argv, argv+argc, "--mpeg")) g_mpeg = 1; +#endif + if (GridCmdOptionExists(argv, argv+argc, "--fps")) { + arg = GridCmdOptionPayload(argv, argv+argc, "--fps"); + GridCmdOptionInt(arg, g_framerate); + } + if (GridCmdOptionExists(argv, argv+argc, "--isosurface")) { + arg = GridCmdOptionPayload(argv, argv+argc, "--isosurface"); + GridCmdOptionFloat(arg, default_contour); + } + + int NoTime = 0, Nd = Grid.Nd(); + Coordinate Slice(Nd,0), SumDims(Nd,0), LoopDims(Nd,0), XYZDims({0,1,2}); + + if (GridCmdOptionExists(argv, argv+argc, "--slice")) { + arg = GridCmdOptionPayload(argv, argv+argc, "--slice"); + GridCmdOptionIntVector(arg, Slice); + } + if (GridCmdOptionExists(argv, argv+argc, "--sum")) { + arg = GridCmdOptionPayload(argv, argv+argc, "--sum"); + GridCmdOptionIntVector(arg, SumDims); + } + if (GridCmdOptionExists(argv, argv+argc, "--loop")) { + arg = GridCmdOptionPayload(argv, argv+argc, "--loop"); + GridCmdOptionIntVector(arg, LoopDims); + } + if (GridCmdOptionExists(argv, argv+argc, "--xyz")) { + arg = GridCmdOptionPayload(argv, argv+argc, "--xyz"); + GridCmdOptionIntVector(arg, XYZDims); + } + if (GridCmdOptionExists(argv, argv+argc, "--notime")) { NoTime = 1; } + + std::thread cmdThread(CommandReaderThread); + cmdThread.detach(); + + // ── VTK scene ──────────────────────────────────────────────────────────── + vtkNew 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()); + + vtkNew renWin; + vtkNew iren; + iren->SetRenderWindow(renWin); + + int frameCount = (int)file_list.size(); + for (int d = 0; d < Nd; d++) if (LoopDims[d]) frameCount *= latt_size[d]; + + vtkNew 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 nrm, rms, contour; + { LatticeComplexD data(&Grid); readFile(data, file_list[0]); nrm = norm2(data); } + rms = std::sqrt(nrm / Grid.gSites()); + contour = default_contour * rms; + + vtkNew imageData; + imageData->SetDimensions(latt_size[XYZDims[0]], latt_size[XYZDims[1]], latt_size[XYZDims[2]]); + imageData->AllocateScalars(VTK_DOUBLE, 1); + for (int xx=0;xxSetScalarComponentFromDouble(xx,yy,zz,0,0.0); + + 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); + + 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()); + + vtkNew outlineData; outlineData->SetInputData(imageData); + vtkNew mapOutline; mapOutline->SetInputConnection(outlineData->GetOutputPort()); + vtkNew outline; outline->SetMapper(mapOutline); + outline->GetProperty()->SetColor(colors->GetColor3d("Black").GetData()); + + vtkNew TextT; + TextT->SetInput("Initialising..."); + TextT->SetPosition(0, 0.7*1025); + TextT->GetTextProperty()->SetFontSize(24); + TextT->GetTextProperty()->SetColor(colors->GetColor3d("Gold").GetData()); + + aRenderer->AddActor(TextT); aRenderer->AddActor(outline); + aRenderer->AddActor(pos); aRenderer->AddActor(neg); + + vtkNew fu; + fu->SetGrid(&Grid); fu->SetFiles(file_list); fu->SetSlice(Slice); + fu->SetSumDimensions(SumDims); fu->SetLoopDimensions(LoopDims); + fu->SetDisplayDimensions(XYZDims); fu->SetSliceDimensions(); + fu->imageData = imageData; fu->text = TextT; fu->maxCount = frameCount; + fu->posExtractor = posExtractor; fu->negExtractor = negExtractor; fu->rms = rms; + + iren->AddObserver(vtkCommand::TimerEvent, fu); + iren->AddObserver(vtkCommand::KeyPressEvent, fu); + + aRenderer->SetActiveCamera(aCamera); aRenderer->ResetCamera(); + aRenderer->SetBackground(colors->GetColor3d("BkgColor").GetData()); + aCamera->Dolly(1.0); aRenderer->SetViewport(0.0,0.0,1.0,1.0); + aRenderer->ResetCameraClippingRange(); + renWin->SetSize(1024,1024); renWin->SetWindowName("ControlledFieldDensity"); + renWin->Render(); iren->Initialize(); + + // Slider + vtkSmartPointer sliderRep = vtkSmartPointer::New(); + sliderRep->SetMinimumValue(0.0); sliderRep->SetMaximumValue(10.0); sliderRep->SetValue(default_contour); + sliderRep->SetTitleText("Fraction RMS"); + sliderRep->GetTitleProperty()->SetColor( colors->GetColor3d("AliceBlue").GetData()); + sliderRep->GetLabelProperty()->SetColor( colors->GetColor3d("AliceBlue").GetData()); + sliderRep->GetSelectedProperty()->SetColor(colors->GetColor3d("DeepPink").GetData()); + 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); + sliderRep->GetPoint1Coordinate()->SetCoordinateSystemToNormalizedDisplay(); sliderRep->GetPoint1Coordinate()->SetValue(0.1,0.1); + sliderRep->GetPoint2Coordinate()->SetCoordinateSystemToNormalizedDisplay(); sliderRep->GetPoint2Coordinate()->SetValue(0.9,0.1); + + vtkSmartPointer sliderWidget = vtkSmartPointer::New(); + sliderWidget->SetInteractor(iren); sliderWidget->SetRepresentation(sliderRep); + sliderWidget->SetAnimationModeToAnimate(); sliderWidget->EnabledOn(); + + vtkSmartPointer slidercallback = vtkSmartPointer::New(); + slidercallback->fu = fu; SliderCallback::contour = default_contour; + sliderWidget->AddObserver(vtkCommand::InteractionEvent, slidercallback); + + // CommandHandler on fast poll timer + vtkNew cmdHandler; + cmdHandler->fu = fu; + cmdHandler->camera = aCamera; + cmdHandler->renderer = aRenderer; + cmdHandler->iren = iren; + cmdHandler->sliderRep = sliderRep; + cmdHandler->isosurfaceLevel = default_contour; + iren->AddObserver(vtkCommand::TimerEvent, cmdHandler); + cmdHandler->pollTimerId = iren->CreateRepeatingTimer(100); + + if (g_mpeg == 0 && NoTime == 0) { + fu->timerId = iren->CreateRepeatingTimer(10000 / g_framerate); + } + + if (g_mpeg) { +#ifdef MPEG + vtkWindowToImageFilter* imageFilter = vtkWindowToImageFilter::New(); + imageFilter->SetInput(renWin); imageFilter->SetInputBufferTypeToRGB(); + vtkFFMPEGWriter* writer = vtkFFMPEGWriter::New(); + writer->SetFileName("movie.avi"); writer->SetRate(g_framerate); + writer->SetInputConnection(imageFilter->GetOutputPort()); writer->Start(); + for (int i = 0; i < fu->maxCount; i++) { + fu->Execute(iren, vtkCommand::TimerEvent, &fu->timerId); + imageFilter->Modified(); writer->Write(); + } + writer->End(); writer->Delete(); imageFilter->Delete(); +#else + assert(-1 && "MPEG support not compiled"); +#endif + } else { + iren->Start(); + } + + g_running = false; + Grid_finalize(); + return EXIT_SUCCESS; +}