diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 708c6d6a8b1..d28aae24256 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -292,6 +292,7 @@ set(STANDARD_PACKAGES EXTRA-MOLECULE EXTRA-PAIR FEP + GRAPHICS GRANULAR H5MD INTERLAYER @@ -533,29 +534,6 @@ if(PKG_ML-QUIP OR PKG_ML-POD OR PKG_ELECTRODE OR PKG_RHEO OR BUILD_TOOLS) endif() endif() -# tweak jpeg library names to avoid linker errors with MinGW cross-compilation -set(JPEG_NAMES libjpeg libjpeg-62) -find_package(JPEG QUIET) -option(WITH_JPEG "Enable JPEG support" ${JPEG_FOUND}) -if(WITH_JPEG) - find_package(JPEG REQUIRED) - target_compile_definitions(lammps PRIVATE -DLAMMPS_JPEG) - target_link_libraries(lammps PRIVATE JPEG::JPEG) -endif() - -find_package(PNG QUIET) -find_package(ZLIB QUIET) -if(PNG_FOUND AND ZLIB_FOUND) - option(WITH_PNG "Enable PNG support" ON) -else() - option(WITH_PNG "Enable PNG support" OFF) -endif() -if(WITH_PNG) - find_package(PNG REQUIRED) - find_package(ZLIB REQUIRED) - target_link_libraries(lammps PRIVATE PNG::PNG ZLIB::ZLIB) - target_compile_definitions(lammps PRIVATE -DLAMMPS_PNG) -endif() find_program(GZIP_EXECUTABLE gzip) find_package_handle_standard_args(GZIP REQUIRED_VARS GZIP_EXECUTABLE) @@ -568,16 +546,6 @@ if(WITH_GZIP) endif() endif() -find_program(FFMPEG_EXECUTABLE ffmpeg) -find_package_handle_standard_args(FFMPEG REQUIRED_VARS FFMPEG_EXECUTABLE) -option(WITH_FFMPEG "Enable FFMPEG support" ${FFMPEG_FOUND}) -if(WITH_FFMPEG) - if(FFMPEG_FOUND OR ((CMAKE_SYSTEM_NAME STREQUAL "Windows") AND CMAKE_CROSSCOMPILING)) - target_compile_definitions(lammps PRIVATE -DLAMMPS_FFMPEG) - else() - message(FATAL_ERROR "ffmpeg executable not found") - endif() -endif() if(BUILD_SHARED_LIBS) set(CONFIGURE_REQUEST_PIC "--with-pic") @@ -589,7 +557,7 @@ else() set(CUDA_REQUEST_PIC) endif() -foreach(PKG_WITH_INCL KSPACE PYTHON ML-IAP VORONOI COLVARS ML-HDNNP MDI MOLFILE NETCDF +foreach(PKG_WITH_INCL GRAPHICS KSPACE PYTHON ML-IAP VORONOI COLVARS ML-HDNNP MDI MOLFILE NETCDF PLUMED QMMM ML-QUIP SCAFACOS MACHDYN VTK KIM COMPRESS ML-PACE LEPTON EXTRA-COMMAND) if(PKG_${PKG_WITH_INCL}) include(Packages/${PKG_WITH_INCL}) diff --git a/cmake/Modules/Packages/GRAPHICS.cmake b/cmake/Modules/Packages/GRAPHICS.cmake new file mode 100644 index 00000000000..40a058b950c --- /dev/null +++ b/cmake/Modules/Packages/GRAPHICS.cmake @@ -0,0 +1,34 @@ +# tweak jpeg library names to avoid linker errors with MinGW cross-compilation +set(JPEG_NAMES libjpeg libjpeg-62) +find_package(JPEG QUIET) +option(WITH_JPEG "Enable JPEG support" ${JPEG_FOUND}) +if(WITH_JPEG) + find_package(JPEG REQUIRED) + target_compile_definitions(lammps PRIVATE -DLAMMPS_JPEG) + target_link_libraries(lammps PRIVATE JPEG::JPEG) +endif() + +find_package(PNG QUIET) +find_package(ZLIB QUIET) +if(PNG_FOUND AND ZLIB_FOUND) + option(WITH_PNG "Enable PNG support" ON) +else() + option(WITH_PNG "Enable PNG support" OFF) +endif() +if(WITH_PNG) + find_package(PNG REQUIRED) + find_package(ZLIB REQUIRED) + target_link_libraries(lammps PRIVATE PNG::PNG ZLIB::ZLIB) + target_compile_definitions(lammps PRIVATE -DLAMMPS_PNG) +endif() + +find_program(FFMPEG_EXECUTABLE ffmpeg) +find_package_handle_standard_args(FFMPEG REQUIRED_VARS FFMPEG_EXECUTABLE) +option(WITH_FFMPEG "Enable FFMPEG support" ${FFMPEG_FOUND}) +if(WITH_FFMPEG) + if(FFMPEG_FOUND OR ((CMAKE_SYSTEM_NAME STREQUAL "Windows") AND CMAKE_CROSSCOMPILING)) + target_compile_definitions(lammps PRIVATE -DLAMMPS_FFMPEG) + else() + message(FATAL_ERROR "ffmpeg executable not found") + endif() +endif() diff --git a/cmake/presets/all_off.cmake b/cmake/presets/all_off.cmake index 31674911bdf..b32539c1e7a 100644 --- a/cmake/presets/all_off.cmake +++ b/cmake/presets/all_off.cmake @@ -35,6 +35,7 @@ set(ALL_PACKAGES EXTRA-PAIR FEP GPU + GRAPHICS GRANULAR H5MD INTEL diff --git a/cmake/presets/all_on.cmake b/cmake/presets/all_on.cmake index 75a4ffde870..7c81327317c 100644 --- a/cmake/presets/all_on.cmake +++ b/cmake/presets/all_on.cmake @@ -37,6 +37,7 @@ set(ALL_PACKAGES EXTRA-PAIR FEP GPU + GRAPHICS GRANULAR H5MD INTEL diff --git a/cmake/presets/basic.cmake b/cmake/presets/basic.cmake index e9ce6d6103f..ec3ee6b4db7 100644 --- a/cmake/presets/basic.cmake +++ b/cmake/presets/basic.cmake @@ -1,7 +1,7 @@ # preset that turns on just a few, frequently used packages # this will be compiled quickly and handle a lot of common inputs. -set(ALL_PACKAGES KSPACE MANYBODY MOLECULE RIGID) +set(ALL_PACKAGES GRAPHICS KSPACE MANYBODY MOLECULE RIGID) foreach(PKG ${ALL_PACKAGES}) set(PKG_${PKG} ON CACHE BOOL "" FORCE) diff --git a/cmake/presets/mingw-cross.cmake b/cmake/presets/mingw-cross.cmake index 0776afee9ff..549b652aa3f 100644 --- a/cmake/presets/mingw-cross.cmake +++ b/cmake/presets/mingw-cross.cmake @@ -30,6 +30,7 @@ set(WIN_PACKAGES EXTRA-PAIR FEP GPU + GRAPHICS GRANULAR INTERLAYER KSPACE diff --git a/cmake/presets/most.cmake b/cmake/presets/most.cmake index 93540700116..f77efa4d4a5 100644 --- a/cmake/presets/most.cmake +++ b/cmake/presets/most.cmake @@ -33,6 +33,7 @@ set(ALL_PACKAGES EXTRA-MOLECULE EXTRA-PAIR FEP + GRAPHICS GRANULAR INTERLAYER KSPACE diff --git a/cmake/presets/windows.cmake b/cmake/presets/windows.cmake index 678b6dafe28..3b2540543e1 100644 --- a/cmake/presets/windows.cmake +++ b/cmake/presets/windows.cmake @@ -28,6 +28,7 @@ set(WIN_PACKAGES EXTRA-MOLECULE EXTRA-PAIR FEP + GRAPHICS GRANULAR INTERLAYER KSPACE diff --git a/doc/doxygen/Doxyfile.in b/doc/doxygen/Doxyfile.in index 33c73fad0de..1439f5b54f6 100644 --- a/doc/doxygen/Doxyfile.in +++ b/doc/doxygen/Doxyfile.in @@ -8,7 +8,6 @@ PROJECT_LOGO = lammps-logo.png CREATE_SUBDIRS = NO ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English -OUTPUT_TEXT_DIRECTION = LTR BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES diff --git a/doc/src/Bibliography.rst b/doc/src/Bibliography.rst index d38c8d9bb83..d788769fc70 100644 --- a/doc/src/Bibliography.rst +++ b/doc/src/Bibliography.rst @@ -230,7 +230,7 @@ Bibliography A Caro, DA Crowson, M Caro; Phys Rev Lett, 95, 075702 (2005) **(CasP)** - CasP webpage: http://www.casp-program.org/ + CasP webpage: https://lief.if.ufrgs.br/pub/CasP/ **(Cawkwell2012)** A.\ M. N. Niklasson, M. J. Cawkwell, Phys. Rev. B, 86 (17), 174308 (2012). diff --git a/doc/src/Build_cmake.rst b/doc/src/Build_cmake.rst index 9eed484db38..f889bd7f0ac 100644 --- a/doc/src/Build_cmake.rst +++ b/doc/src/Build_cmake.rst @@ -233,6 +233,42 @@ for IDEs like Eclipse, CodeBlocks, or Kate can be selected using the ``-G`` command-line flag. A list of available generator settings for your specific CMake version is given when running ``cmake --help``. +.. _cmake_targets: + +Build targets +^^^^^^^^^^^^^ + +The LAMMPS CMake files are set up to have multiple "targets", one for +each component that is set up to be built. If you do not provide an +explicit target when building, the target "all" is assumed. This should +build all configured targets and include re-running CMake to update the +build files when files have changed or new files were added which could +require updates in the build process. The most important individual +targets are "lammps" to build the LAMMPS library and "lmp" to build the +LAMMPS executable. If you want to selectively want to update only one +of them you can use the command: + +.. code-block:: bash + + cmake --build build --target lmp + +Especially, when programming LAMMPS and you are making just local +changes, or only want to make certain that the code still compiles +and links, you may want to skip may of the extra steps that are +run by default and then append "/fast" to the target. Example: + +.. code-block:: bash + + cmake --build build --target lammps/fast + +Note that this speeds up the build by skipping a lot of checks for +dependencies, and avoiding to re-run CMake, so you may occasionally need +to compile without the "/fast" suffix or use the "all" (= default) +target to update everything. + +Some custom targets, e.g. "install-python" are explicitly excluded +from the "all" target and must be built individually. + .. _cmake_multiconfig: Multi-configuration build systems @@ -241,7 +277,7 @@ Multi-configuration build systems Throughout this manual, it is mostly assumed that LAMMPS is being built on a Unix-like operating system with "make" as the underlying "builder", since this is the most common case. In this case the build -"configuration" is chose using ``-D CMAKE_BUILD_TYPE=`` +"configuration" is chosen using ``-D CMAKE_BUILD_TYPE=`` with ```` being one of "Release", "Debug", "RelWithDebInfo", or "MinSizeRel". Some build tools, however, can also use or even require having a so-called multi-configuration build system diff --git a/doc/src/Build_development.rst b/doc/src/Build_development.rst index 9cf7d7b9c60..a6c3b2fecb1 100644 --- a/doc/src/Build_development.rst +++ b/doc/src/Build_development.rst @@ -5,6 +5,7 @@ The build procedures in LAMMPS offers a few extra options which are useful during development, testing or debugging. .. contents:: + :local: ---------- diff --git a/doc/src/Build_extras.rst b/doc/src/Build_extras.rst index b8c18625bc4..a853bef649e 100644 --- a/doc/src/Build_extras.rst +++ b/doc/src/Build_extras.rst @@ -41,6 +41,7 @@ This is the list of packages that may require additional steps. * :ref:`COLVARS ` * :ref:`COMPRESS ` * :ref:`ELECTRODE ` + * :ref:`GRAPHICS ` * :ref:`GPU ` * :ref:`H5MD ` * :ref:`INTEL ` @@ -110,6 +111,83 @@ versions use an incompatible API and thus LAMMPS will fail to compile. ---------- +.. _graphics: + +GRAPHICS package +---------------- + +.. versionadded:: TBD + + *dump image*\ , *dump_movie* and supporting classes were moved to form + the new GRAPHICS package together with several *fix graphics/...* styles. + +The :doc:`dump image ` command has options to output JPEG or +PNG image files in addition to the default PPM format, and the :doc:`fix +graphics/labels ` can read images in JPEG or PNG +format in addition to PPM format files. Likewise, the :doc:`dump movie +` command outputs movie files in a variety of movie formats. +Using these additional options requires the following settings: + +.. tabs:: + + .. tab:: CMake build + + .. code-block:: bash + + -D WITH_JPEG=value # yes or no + # default = yes if CMake finds JPEG development files, else no + -D WITH_PNG=value # yes or no + # default = yes if CMake finds PNG and ZLIB development files, + # else no + -D WITH_FFMPEG=value # yes or no + # default = yes if CMake can find ffmpeg, else no + + Usually these settings are all that is needed. If CMake cannot + find the graphics header, library, executable files, you can set + these variables: + + .. code-block:: bash + + -D JPEG_INCLUDE_DIR=path # path to jpeglib.h header file + -D JPEG_LIBRARY=path # path to libjpeg.a (.so) file + -D PNG_INCLUDE_DIR=path # path to png.h header file + -D PNG_LIBRARY=path # path to libpng.a (.so) file + -D ZLIB_INCLUDE_DIR=path # path to zlib.h header file + -D ZLIB_LIBRARY=path # path to libz.a (.so) file + -D FFMPEG_EXECUTABLE=path # path to ffmpeg executable + + .. tab:: Traditional make + + .. code-block:: make + + LMP_INC = -DLAMMPS_JPEG -DLAMMPS_PNG -DLAMMPS_FFMPEG + + JPG_INC = -I/usr/local/include # path to jpeglib.h, png.h, zlib.h headers + # if make cannot find them + JPG_PATH = -L/usr/lib # paths to libjpeg.a, libpng.a, libz.a (.so) + # files if make cannot find them + JPG_LIB = -ljpeg -lpng -lz # library names + + As with CMake, you do not need to set ``JPG_INC`` or ``JPG_PATH``, + if make can find the graphics header and library files in their + default system locations. You must specify ``JPG_LIB`` with a + list of graphics libraries to include in the link. You must make + certain that the ffmpeg executable (or ffmpeg.exe on Windows) is + in a directory where LAMMPS can find it at runtime; that is + usually a directory list in your ``PATH`` environment variable. + +Using ``ffmpeg`` to output movie files requires that your machine +supports the "popen" function in the standard runtime library. + +.. note:: + + On some clusters with high-speed networks, using the fork() + library call (required by popen()) can interfere with the fast + communication library and lead to simulations using ffmpeg to hang or + crash. + +---------- + .. _gpu: GPU package diff --git a/doc/src/Build_package.rst b/doc/src/Build_package.rst index 3b707c67257..7e863ade126 100644 --- a/doc/src/Build_package.rst +++ b/doc/src/Build_package.rst @@ -148,8 +148,8 @@ other files dependent on that package are also excluded. however, if you downloaded a tarball, 3 packages (KSPACE, MANYBODY, MOLECULE) were pre-installed via the traditional make procedure in the ``src`` directory. That is no longer the case, so that CMake - will build as-is without needing to first uninstall those - packages. You can quickly include those packages (plus RIGID) by + will build as-is without needing to first uninstall those packages. + You can quickly include those packages (plus RIGID and GRAPHICS) by using the "basic" preset with CMake or ``make yes-basic`` with traditional make as discussed below. @@ -174,7 +174,7 @@ make a copy of one of them and modify it to suit your needs. .. code-block:: bash - # enable just a few core packages + # enable just a few core packages (MOLECULE, KSPACE, MANYBODY, RIGID, GRAPHICS) cmake -C ../cmake/presets/basic.cmake [OPTIONS] ../cmake # enable most packages diff --git a/doc/src/Build_settings.rst b/doc/src/Build_settings.rst index f057d218250..4c6d2918b80 100644 --- a/doc/src/Build_settings.rst +++ b/doc/src/Build_settings.rst @@ -12,7 +12,6 @@ explains how to do this for building both with CMake and make. * `FFT library`_ for use with the :doc:`kspace_style pppm ` command * `Size of LAMMPS integer types and size limits`_ * `Read or write compressed files`_ -* `Output of JPEG, PNG, and movie files`_ via the :doc:`dump image ` or :doc:`dump movie ` commands * `Support for downloading files from the input`_ * `Prevent download of large potential files`_ * `Memory allocation alignment`_ @@ -384,76 +383,6 @@ in whichever ``lib/gpu/Makefile`` is used must be the same as above. ---------- -.. _graphics: - -Output of JPEG, PNG, and movie files ------------------------------------- - -The :doc:`dump image ` command has options to output JPEG or -PNG image files. Likewise, the :doc:`dump movie ` command -outputs movie files in a variety of movie formats. Using these options -requires the following settings: - -.. tabs:: - - .. tab:: CMake build - - .. code-block:: bash - - -D WITH_JPEG=value # yes or no - # default = yes if CMake finds JPEG development files, else no - -D WITH_PNG=value # yes or no - # default = yes if CMake finds PNG and ZLIB development files, - # else no - -D WITH_FFMPEG=value # yes or no - # default = yes if CMake can find ffmpeg, else no - - Usually these settings are all that is needed. If CMake cannot - find the graphics header, library, executable files, you can set - these variables: - - .. code-block:: bash - - -D JPEG_INCLUDE_DIR=path # path to jpeglib.h header file - -D JPEG_LIBRARY=path # path to libjpeg.a (.so) file - -D PNG_INCLUDE_DIR=path # path to png.h header file - -D PNG_LIBRARY=path # path to libpng.a (.so) file - -D ZLIB_INCLUDE_DIR=path # path to zlib.h header file - -D ZLIB_LIBRARY=path # path to libz.a (.so) file - -D FFMPEG_EXECUTABLE=path # path to ffmpeg executable - - .. tab:: Traditional make - - .. code-block:: make - - LMP_INC = -DLAMMPS_JPEG -DLAMMPS_PNG -DLAMMPS_FFMPEG - - JPG_INC = -I/usr/local/include # path to jpeglib.h, png.h, zlib.h headers - # if make cannot find them - JPG_PATH = -L/usr/lib # paths to libjpeg.a, libpng.a, libz.a (.so) - # files if make cannot find them - JPG_LIB = -ljpeg -lpng -lz # library names - - As with CMake, you do not need to set ``JPG_INC`` or ``JPG_PATH``, - if make can find the graphics header and library files in their - default system locations. You must specify ``JPG_LIB`` with a - list of graphics libraries to include in the link. You must make - certain that the ffmpeg executable (or ffmpeg.exe on Windows) is - in a directory where LAMMPS can find it at runtime; that is - usually a directory list in your ``PATH`` environment variable. - -Using ``ffmpeg`` to output movie files requires that your machine -supports the "popen" function in the standard runtime library. - -.. note:: - - On some clusters with high-speed networks, using the fork() - library call (required by popen()) can interfere with the fast - communication library and lead to simulations using ffmpeg to hang or - crash. - ----------- - .. _gzip: Read or write compressed files diff --git a/doc/src/Commands_fix.rst b/doc/src/Commands_fix.rst index c810d5b0334..f5e427fe3c7 100644 --- a/doc/src/Commands_fix.rst +++ b/doc/src/Commands_fix.rst @@ -83,7 +83,11 @@ OPT. * :doc:`gjf ` * :doc:`gld ` * :doc:`gle ` - * :doc:`graphics ` + * :doc:`graphics/arrows ` + * :doc:`graphics/isosurface ` + * :doc:`graphics/labels ` + * :doc:`graphics/objects ` + * :doc:`graphics/periodic ` * :doc:`gravity (ko) ` * :doc:`grem ` * :doc:`halt ` diff --git a/doc/src/Commands_removed.rst b/doc/src/Commands_removed.rst index ad5e1596045..6c0659f4ca3 100644 --- a/doc/src/Commands_removed.rst +++ b/doc/src/Commands_removed.rst @@ -2,6 +2,7 @@ Removed commands and packages ============================= .. contents:: + :local: ------ diff --git a/doc/src/Developer_notes.rst b/doc/src/Developer_notes.rst index 8dc01331554..7807a2b494b 100644 --- a/doc/src/Developer_notes.rst +++ b/doc/src/Developer_notes.rst @@ -8,6 +8,7 @@ code does, or what a function does and its input/outputs. The topics on this page are intended to document code functionality at a higher level. .. contents:: Available notes + :local: ---- diff --git a/doc/src/Errors_details.rst b/doc/src/Errors_details.rst index 04a4f19a3aa..1ab91838b07 100644 --- a/doc/src/Errors_details.rst +++ b/doc/src/Errors_details.rst @@ -13,6 +13,7 @@ this page. ------- .. contents:: Individual paragraphs + :local: ------ diff --git a/doc/src/Howto_body.rst b/doc/src/Howto_body.rst index 23d0e1019d8..e5ee25a52a9 100644 --- a/doc/src/Howto_body.rst +++ b/doc/src/Howto_body.rst @@ -186,7 +186,7 @@ with this body style to compute body/body and body/non-body interactions. The *rounded/polygon* body style represents body particles as a 2d polygon with a variable number of N vertices. This style can only be -used for 2d models; see the :doc:`boundary ` command. See the +used for 2d models; see the :doc:`dimension ` command. See the :doc:`pair_style body/rounded/polygon ` page for a diagram of two squares with rounded circles at the vertices. Special cases for N = 1 (circle) and N = 2 (rod with rounded ends) can also be specified. @@ -494,16 +494,17 @@ current COM and orientation of the body particle. The :doc:`dump image ` command and its *body* keyword can be used to render body particles. -For the *nparticle* body style, each body is drawn as a -collection of spheres, one for each sub-particle. The size of each -sphere is determined by the *bflag1* parameter for the *body* keyword. -The *bflag2* argument is ignored. +For the *nparticle* body style, each body is drawn as a collection of +spheres, one for each sub-particle. The size of each sphere is +determined by the *bflag1* parameter for the *body* keyword. The +*bflag2* argument is ignored. For the *rounded/polygon* body style, each body is drawn as a polygon -with N line segments. For the *rounded/polyhedron* body style, each -face of each body is drawn as a polygon with N line segments. The -drawn diameter of each line segment is determined by the *bflag1* -parameter for the *body* keyword. The *bflag2* argument is ignored. +and for *rounded/polyhedron* as a polyhedron. The *bflag2* argument +selects how the bodies are represented: for a value of 1, only the faces +are drawn; for a value of 2, only a wireframe mesh is drawn, and for a +value of 3 both. The diameter of each wireframe line is determined by +the *bflag1* parameter for the *body* keyword. Note that for both the *rounded/polygon* and *rounded/polyhedron* styles, line segments are drawn between the pairs of vertices. diff --git a/doc/src/Howto_cmake.rst b/doc/src/Howto_cmake.rst index 55f3c9f4add..9146bb79405 100644 --- a/doc/src/Howto_cmake.rst +++ b/doc/src/Howto_cmake.rst @@ -394,14 +394,14 @@ change some variables later with additional *-D* flags. A few examples: cmake -C ../cmake/presets/basic.cmake -D BUILD_MPI=off ../cmake The first command will install the packages ``KSPACE``, ``MANYBODY``, -``MOLECULE``, ``RIGID`` and ``MISC``; the first four from the preset -file and the fifth from the explicit variable definition. The second -command will first switch the compiler toolchain to use the Clang -compilers and install a large number of packages that are not depending -on any special external libraries or tools and are not very unusual. -The third command will enable the first four packages like above and -then enforce compiling LAMMPS as a serial program (using the MPI STUBS -library). +``MOLECULE``, ``RIGID``, ``GRAPHICS``, and ``MISC``; the first five from +the preset file and the fifth from the explicit variable definition. +The second command will first switch the compiler toolchain to use the +Clang compilers and install a large number of packages that are not +depending on any special external libraries or tools and are not very +unusual. The third command will enable the first four packages like +above and then enforce compiling LAMMPS as a serial program (using the +MPI STUBS library). It is also possible to do this incrementally. diff --git a/doc/src/Howto_python.rst b/doc/src/Howto_python.rst index d8f858d6bf5..62d38ae4c25 100644 --- a/doc/src/Howto_python.rst +++ b/doc/src/Howto_python.rst @@ -2,6 +2,7 @@ LAMMPS Python Tutorial ====================== .. contents:: + :local: ----- diff --git a/doc/src/Howto_viz.rst b/doc/src/Howto_viz.rst index 0166f1170e4..ea80da342c9 100644 --- a/doc/src/Howto_viz.rst +++ b/doc/src/Howto_viz.rst @@ -5,10 +5,11 @@ Snapshots from LAMMPS simulations can be viewed, visualized, and analyzed in a variety of ways. LAMMPS snapshots are created by the :doc:`dump ` command, which -can create files in several formats. The native LAMMPS dump format is a -text file (see :lammps:`dump atom` or :lammps:`dump custom`) which can be visualized by -`several visualization tools `_ for MD -simulation trajectories. `OVITO `_ and `VMD +can create files in several formats. The native LAMMPS dump format is a +text file (see :lammps:`dump atom` or :lammps:`dump custom`) which can +be visualized by `several visualization tools +`_ for MD simulation trajectories. +`OVITO `_ and `VMD `_ seem to be the most popular choices among them. @@ -17,11 +18,884 @@ styles can output internally rendered images or convert them to a movie during the MD run. It is also possible to create visualizations from LAMMPS inputs or restart file with `LAMMPS-GUI `_, which uses the :doc:`dump image -` command internally. The ``Snapshot Image Viewer`` in -LAMMPS-GUI can be used to adjust the visualization settings of the -current system interactively and then export the corresponding LAMMPS -commands to the clipboard to be inserted into an input file. - -Programs included with LAMMPS as auxiliary tools can convert -between LAMMPS format files and other formats. See the :doc:`Tools -` page for details. These are rarely needed these days. +` command internally. If the LAMMPS input already contains +a :doc:`dump image ` command, the resulting images will be +noted by LAMMPS-GUI and can be viewed and animated directly in the +``Slide Show`` dialog. The images can be transformed (i.e. scaled, +mirrored, or rotated) and exported into a video, too. The ``Image +Viewer`` dialog in LAMMPS-GUI can be used to visualize the *current* +system, adjust a variety of visualization settings quasi-interactively +from the GUI. + +Programs included with LAMMPS as auxiliary tools can convert between +LAMMPS format files and other formats so they can be read by certain +programs. See the :doc:`Tools ` page for details. These are +rarely needed these days. + +------------------------ + +Advanced graphics features in the *dump image* command +====================================================== + +.. versionadded:: TBD + +The following paragraphs discuss some of the more advanced features in +the :doc:`dump image ` command in LAMMPS with the help of +some input file examples. For exact details of keywords and arguments, +please refer to the detailed documentation of the respective commands. + +Please note that many of these features were added or significantly +updated after LAMMPS version 10 Dec 2025 and well into the 2026 stable +version development cycle. If you are using an older version of LAMMPS, +these examples may likely cause errors or look differently. + +.. contents:: Available topics + :local: + +------------ + +Complete example inputs +----------------------- + +The discussions below only quote relevant sections of input files to +show specifically the commands used in the visualizations. There are +complete example files in the ``examples/GRAPHICS`` folder of the LAMMPS +source code distribution. + +--------------- + +Image quality and resolution +---------------------------- + +The image resolution is determined by the *size* keyword. The default +setting is to create images with 512x512 pixels. This is rather low +resolution. A typical (non-"retina") computer screen at the time of +writing this howto has about 100 dpi (= dots per inch) so that this +image covers an area of a bit over 5x5 inches (or about 135x135 +millimeters). Images for use in presentations, posters, or publications +should generally be generated at larger sizes. When the image is meant +for printing, one has to consider that a printer may have a resolution +of 300 dpi or even 600 dpi which means that images should be created 3x3 +or 6x6 times larger to utilize the full resolution of the printer and +show as much details and be as clean as possible. Otherwise images have +to be scaled up which can make them look blurry and with ragged edges. + +The keywords *fsaa* and *ssao* can be used to further improve the image +quality at the expense of significant additional computational cost to +render the images: + +- FSAA stands for `Full Scene Anti-Aliasing + `_ + and means in the case of LAMMPS that the image is rendered at four + times the size (double the width and double the height) and then the + pixels in the final image are computed by taking the average of 2x2 + pixel groups. This will result in smoother, less ragged edges of + objects in the image. + +- SSAO stands for `Screen Space Ambient Occlusion + `_ and + is an image enhancement technique that uses intermediate image data + (like the information of how close or far away a pixel is to the + camera and its neighboring pixels) and brightens or darkens randomly + selected pixels in its neighborhood based on that information. This + enhances the depth perception of objects in an image. + +Both methods are complementary and thus can be combined for additional +improvement of the image quality. The images below show from left to +right the same excerpt of a dump image output with the default settings, +with *fsaa* enabled, with *ssao* enabled, and with both features +enabled. + +.. |imagequality1| image:: JPG/image.default.png + :width: 24% +.. |imagequality2| image:: JPG/image.fsaa.png + :width: 24% +.. |imagequality3| image:: JPG/image.ssao.png + :width: 24% +.. |imagequality4| image:: JPG/image.both.png + :width: 24% + +|imagequality1| |imagequality2| |imagequality3| |imagequality4| + +The computational cost to create the images with :doc:`dump image +` depends on the image size, the number of objects to be +rendered (this number can grow quickly when using fine triangle meshes), +and the choice of the *fsaa* and *ssao* settings. For high resolution +images, a correspondingly large image size has to be chosen. + +Since the simulation has to wait for the dump image command to complete +its image rendering, creating high resolution, high quality images can +slow down a simulation significantly with frequent output of images. On +the other hand, the image rasterizer in LAMMPS is fairly simple and thus +fast compared to more advanced image generation tools like ray tracers. +The method it uses to generate the images allows to have each MPI +process create image data for the atoms and objects they "own" and then +this image data is merged for the final output with a procedure that has +O(log(N)) complexity with the number of MPI processes. At the moment +there is no GPU acceleration and only limited multi-threading +parallelization available (e.g. for SSAO post-processing of image data). + +-------------------- + +Color selection and color management +------------------------------------ + +The :doc:`dump image ` command in LAMMPS has a variety +of options to assign colors to the rendered graphics. In most cases +the color is assigned to atom (or bond) types and uses a default map +with six colors as follows: + +* type 1 = red +* type 2 = green +* type 3 = blue +* type 4 = yellow +* type 5 = aqua +* type 6 = cyan + +and repeats itself for types :math:`> 6`. This mapping can be changed by the +"dump_modify acolor" command, though. If you want to change the color of a +specific atom type, you can use :doc:`dump modify acolor `. For +example to color atoms of type 1 in gray and type 2 in white, you would use: + +.. code-block:: LAMMPS + + dump_modify img acolor 1 gray acolor 2 white + +There are 144 predefined colors, but you can add new colors or modify +existing ones, too, with the *dump_modify color* keyword. The *color* +keyword is followed by the name of the color and the intensity of the +red, green, and blue components (R/G/B) in a range from 0.0 to 1.0. Here +is an example to create eight new color names followed by the *acolor* +keyword with a wildcard to replace the default map of six atom colors +with a new map of the either newly defined colors. + +.. code-block:: LAMMPS + + dump_modify viz color map1 0.012 0.016 0.322 color map2 0.008 0.243 0.541 & + color map3 0 0.467 0.714 color map4 0 0.588 0.780 color map5 0 0.706 0.847 & + color map6 0.282 0.792 0.894 color map7 0.565 0.878 0.937 color map8 0.792 0.941 0.973 & + acolor * map1/map2/map3/map4/map5/map6/map7/map8 + +Yet another option is to color the atoms by element. The per-element +colors are predefined but LAMMPS does not know which element an atom +type corresponds to and by default uses carbon for all atom types. The +correct element information needs to be provided with a *dump_modify +element* command followed by an element name for each atom type. In case +of the ``peptide`` example bundled with LAMMPS this would be: + +.. code-block:: LAMMPS + + dump viz peptide image 1000 image-*.png element type size 600 600 zoom 2.0 + dump_modify viz element C C O H N C C C O H H S O H + +Finally, atoms can be colored by the value of a per-atom property using +a color map. There are several variants of color maps. Here is a +simple example coloring the atoms in the ``peptide`` example by their +charge with a continuous color map where white is neutral, red positive, +blue negative, and the color intensity corresponds to the magnitude of +the charge: + +.. code-block:: LAMMPS + + dump viz peptide image 1000 image-*.png q type size 600 600 zoom 2.0 + dump_modify amap -1.0 1.0 ca 0 3 min blue 0.0 white max red + + +.. |colors1| image:: img/colors-default.png + :width: 24% +.. |colors2| image:: img/colors-newmap.png + :width: 24% +.. |colors3| image:: img/colors-element.png + :width: 24% +.. |colors4| image:: img/colors-map.png + :width: 24% + +|colors1| |colors2| |colors3| |colors4| + +.. raw:: html + +
(Different colorization styles. Left to right: by default + type map, by custom type map, by element, and by charge. Click to see + the full-size images)
+ +-------------------- + +Transparency +------------ + +It is now possible to create approximately transparent graphics objects +using an `ordered dithering algorithm +`_ which results in a +so-called *screen-door transparency* effect. In essence, for a +transparent object only a part of the pixels are drawn and thus exposing +any object behind the transparent object where drawing the pixels is +skipped. LAMMPS employs a 16x16 Bayer matrix pattern that leads to +rather regular patterns. A benefit of this approach is that it does not +add extra cost to the rendering and for a 25%, 50%, and 75% transparency +setting, there are no visible pixel patterns when also FSAA is enabled. +In this case each pixel is the average of a 2x2 block of pixels in an +image of double the width and height, and thus the transparent object +will contribute 3, 2, or 1 pixels to the 2x2 block which is averaged. + +Transparency is typically - like the color of objects - associated with +an atom type and can be modified through the :doc:`dump_modify atrans +` command and specified as an opacity ratio, i.e. a number +between 0 (fully transparent) and 1 (fully opaque). Other choices are +available and described in the documentation page. + +.. |transparency1| image:: img/transparent-1.png + :width: 49% +.. |transparency2| image:: img/transparent-2.png + :width: 49% + +|transparency1| |transparency2| + +.. raw:: html + +
(Transparency example with opacity set to 0.25, 0.33, 0.5, + 0.75, and 1.0 (left to right): left: without FSAA, right: with + FSAA. Click to see the full-size images)
+ +----------------------- + +Creating and viewing animated GIFs and movie files +-------------------------------------------------- + +A series of JPEG, PNG, or PPM images can be converted into a movie file +and then played as a movie using commonly available tools. Using dump +style *movie* automates this step *and* avoids the intermediate step of +writing (many) image snapshot file. But LAMMPS has to be compiled with +``-DLAMMPS_FFMPEG`` and a compatible FFmpeg executable has to be +installed. When using `LAMMPS-GUI `_ to +run LAMMPS, you can run the simulation and LAMMPS-GUI will automatically +show the created images in its ``Slideshow Viewer`` dialog. From there +you can animate or single step through them and also export them to a +movie file via FFMpeg. + +To manually convert JPEG, PNG or PPM files into an animated GIF or +MPEG or other movie file you can use: + +#. Use the ImageMagick ``convert`` program (called ``magick`` in recent versions). + + .. code-block:: bash + + convert *.jpg foo.gif + convert -loop 1 *.ppm foo.mpg + + Animated GIF files from ImageMagick are not optimized. You can use + a program like gifsicle to optimize and thus massively shrink them. + MPEG files created by ImageMagick are in MPEG-1 format with a rather + inefficient compression and low quality compared to more modern + compression styles like MPEG-4, H.264, VP8, VP9, H.265 and so on. + +#. Use QuickTime. + + Select "Open Image Sequence" under the File menu Load the images into + QuickTime to animate them Select "Export" under the File menu Save the + movie as a QuickTime movie (\*.mov) or in another format. QuickTime + can generate very high quality and efficiently compressed movie + files. Some of the supported formats require to buy a license and some + are not readable on all platforms until specific runtime libraries are + installed. + +#. Use FFmpeg + + `FFMpeg `_ is a command-line tool that is + available on many platforms and allows extremely flexible encoding + and decoding of movies. + + .. code-block:: bash + + cat snap.*.jpg | ffmpeg -y -f image2pipe -c:v mjpeg -i - -b:v 2000k movie.m4v + cat snap.*.ppm | ffmpeg -y -f image2pipe -c:v ppm -i - -b:v 2400k movie.mp4 + + Front ends for FFmpeg exist for multiple platforms. For more + information see the `FFmpeg homepage `_ + +---------- + +Play the movie: + +#. Use your web browser to view an animated GIF or MP4 movie format movie. + + Select "Open File" under the File menu + Load the animated GIF or MP4 movie file + +#. Use the freely available `VideoLAN media player (vlc) + `_ or `FFMpeg player tool (ffplay) + `_ to view a movie. + + Both are available for multiple operating systems and support a large + variety of file formats and decoders. There are plenty more media + player packages available on the different operating systems. + + .. code-block:: bash + + vlc foo.mpg + ffplay bar.avi + +#. Use the `Pizza.py `_ + `animate tool `_, + which works directly on a series of image files. + + .. code-block:: python + + a = animate("foo*.jpg") + +#. QuickTime and other Windows- or macOS-based media players can + obviously play movie files directly. Similarly for corresponding tools + bundled with Linux desktop environments. However, due to licensing + issues with some file formats, the formats may require installing + additional libraries, purchasing a license, or may not be + supported. + +-------------- + +Prototyping dump image visualizations with LAMMPS-GUI +----------------------------------------------------- + +One of the challenges when using :doc:`dump image ` for +creating visualizations compared to the likes of `OVITO +`_ and `VMD +`_ is that it is non-interactive +and it can be tedious and time consuming to find suitable settings for +camera view or zoom factor and others for a specific system. You would +have to run LAMMPS to create an image, then view it in an image viewer +program, edit the input file and repeat the process until you have found +settings that you like. + +This process can be streamlined with `LAMMPS-GUI +`_, which does not contain its own +visualization code, but rather uses :doc:`dump image ` +through the :doc:`LAMMPS C-language library interface ` and +displays the resulting images for its `Image Viewer Dialog +`_. Through the GUI +elements and dialogs many settings can be adjusted and then the image +will be recreated with the updated settings and thus allowing to refine +a visualization in a quasi-interactive fashion. The resulting command +lines can be transferred to the cut-n-paste buffer of the windowing +system and pasted into the input file and then further adjusted. + +Once the input contains a :doc:`dump image ` command, +LAMMPS-GUI will notice when a new image has been created and load it +into "Slide Show Dialog". This streamlines the process of building more +complex visualizations once you have copied it into the input since you +have editor and image viewer as part of the same program and can quickly +start and stop LAMMPS with a mouse click or keystroke. A large part of +the visualization examples on shown in this Howto page have been created +this way. + +.. |gui1| image:: JPG/lammps-gui-main.png + :width: 38% +.. |gui2| image:: JPG/lammps-gui-image.png + :width: 32% +.. |gui3| image:: JPG/lammps-gui-slideshow.png + :width: 28% + +|gui1| |gui2| |gui3| + +-------------- + +Visualizing systems using potentials with implicit bonds +-------------------------------------------------------- + +There are several pair styles available in LAMMPS where the bond +information is not taken from from the bond topology in a data file but +the potentials first determine a "bond-order" parameter for pairs of +atoms and - depending on the value of that parameter - apply forces for +bonded interactions. This applies to :doc:`ReaxFF `, +:doc:`REBO and AIREBO `, :doc:`BOP `, and several +others pair styles. These implicit bonds will not be shown by +:doc:`dump image ` since its mechanism for displaying bonds +relies on explicit bonds being present in the bond topology. + +One can hide the fact that there are no bonds by setting the atom radii +to the covalent radii of the corresponding elements (see leftmost +example image below). This will result in a representation often +labeled as "VDW" in popular visualization tools. Otherwise, there are +currently three approaches to make those bonds visible. + +#. Access the (internal) bond order information from the pair style + through a custom fix and then use the *fix* keyword of the :doc:`dump + image ` command to use the graphics objects information + provided by the fix to visualize the bonds (see below for more + information). That includes bonds that are broken and formed. This + is the most accurate option since it uses the data that is used by + the computation of the model. This is currently only available for + ReaxFF by using :doc:`fix reaxff/bonds `. + +#. Use the *autobonds* keyword of :doc:`dump image ` to + approximate the bonds based on a simple distance heuristic. This is + similar to the *Dynamic Bonds* representation in `VMD + `_. How accurate this option + will be depends on the complexity of the system and how many + different bond lengths there are. For the simple water system shown + below, there are no significant differences, since there is only one + type of bond (between oxygen and hydrogen). The *autobond* keyword + uses on top of the distance cutoff between atoms, the heuristic that + no bonds will be drawn between two hydrogen atoms and thus bogus + bonds are avoided when using a larger bond cutoff (e.g. suitable for + carbon-carbon bonds) which is larger than the typical + hydrogen-hydrogen distance for hydrogen atoms bound to the same atom + (e.g. in water, methane or hydrocarbon chains). + +#. Use use a combination of :doc:`fix bond/break ` + and :doc:`fix bond/create/angle ` with :doc:`bond + style zero ` to dynamically create and remove bonds that + do not add any forces. This also requires to tell the neighbor list + code to not treat any pairs of atoms as special neighbors (otherwise + the corresponding pairs of atoms could be excluded from the neighbor + list and thus the forces computed by the pair style incorrect) + through using the :doc:`special_bonds ` command. + Unlike the two other options which were recently added when this + document when was written, this method also works with older versions + of LAMMPS. Here is an example of the necessary commands for a carbon + nanotube (that is modeled with AIREBO): + + .. code-block:: LAMMPS + + bond_style zero + bond_coeff 1 1.4 + special_bonds lj/coul 1.0 1.0 1.0 + fix break all bond/break 1000 1 2.5 + fix form all bond/create/angle 1000 1 1 2.0 1 aconstrain 90.0 180 + + This "graphics hack" was originally posted as part of the LAMMPS + tutorial at + https://lammpstutorials.github.io/sphinx/build/html/tutorial2/breaking-a-carbon-nanotube.html + +.. |bonds0| image:: img/bonds-vdw.png + :width: 18% +.. |bonds1| image:: img/bonds-fix.png + :width: 18% +.. |bonds2| image:: img/bonds-auto.png + :width: 18% +.. |bonds3| image:: img/bonds-create.png + :width: 45% + +|bonds0| |bonds1| |bonds2| |bonds3| + +.. raw:: html + +
(Visualizing systems with implicit bonds. Left to right: + using covalent radii, using fix reaxff/bonds, using autobond, + using fix bond/create/angle. Click to see the full-size + images)
+ +------------- + +.. Visualizing tri or line particles +.. --------------------------------- +.. +.. ------------- + +Visualizing body particles +-------------------------- + +Body particles are objects formed from either a collection of spherical +particles, polygons (in 2d), or polyhedra (in 3d) formed from triangular +or quadrilateral surfaces. The regular :doc:`dump ` command can +only output the center of those bodies (and their orientation), which +complicates the visualization with external tools. In addition, the +position of the constituent particles of *nparticles* bodies or the +positions of the vertices of *rounded/polygon* or *rounded/polyhedron* +bodies, which can be computed with :doc:`compute body/local +` and output with :doc:`dump local `. + +As an alternative, the bodies can be visualized directly with :doc:`dump +image ` using the *body* keyword. Without the *body* +keyword the body particles would be visualized like atoms as single +spheres. The color and transparency settings can be changed by settings +those properties for the corresponding atom types. It is also possible +to represent the bodies as either wireframes (*bflag1* value 2), planar +faces (*bflag1* value 1), or both (*bflag1* value 3). + +.. |body1| image:: img/body-frames.png + :width: 33% +.. |body2| image:: img/body-faces.png + :width: 33% +.. |body3| image:: img/body-both.png + :width: 33% + +|body1| |body2| |body3| + +.. raw:: html + +
(Body particle visualization examples for the + rounded/polyhedron body style left: frames, center: faces, + right: both. Click to see the full-size images)
+ +------------- + +Visualizing ellipsoid particles +------------------------------- + +Ellipsoidal particles are a generalization of spheres that may have +three different radii to define the shape. They can be modeled using +pair styles :doc:`gayberne ` or :doc:`resquared +`. The regular :doc:`dump custom ` command can +output the center of those bodies, the shape parameters and the +orientation as quaternions. If one follows the required conventions and +follows the documented steps, those trajectory dump files can be +`imported and visualized in OVITO +`_ + +As an alternative, the ellipsoid particles can be visualized directly +with :doc:`dump image ` using the *ellipsoid* keyword. The +color and transparency settings can be changed by settings those +properties for the corresponding atom types. It is also possible to +represent the ellipsoids via generating a triangle mesh and visualizing +it as either wireframes (*eflag* value 2), planar faces (*eflag* value +1), or both (*eflag* value 3), same as demonstrated for body particles +above. The use of a triangle mesh is currently required since the +rasterizer built into LAMMPS does not offer a suitable graphics +primitive for ellipsoids. The mesh is constructed by iteratively +refining a triangle mesh representing an octahedron where each triangle +is replaced by four triangles. For a smooth representation a refinement +level of 5 or 6 is required, which will cause a significant slowdown of +the rendering of the image. Also, some artifacts can happen due to +rounding which can be somewhat minimized using FSAA (which causes +further slowdown of the rendering). + +.. |ellipsoid1| image:: img/ellipsoid-level2.png + :width: 33% +.. |ellipsoid2| image:: img/ellipsoid-level4.png + :width: 33% +.. |ellipsoid3| image:: img/ellipsoid-level6.png + :width: 33% + +|ellipsoid1| |ellipsoid2| |ellipsoid3| + +.. raw:: html + +
(Ellipsoid particle visualization examples for different mesh refinement levels. + left: level 2, center: level 4, right: level 6. Click to see the full-size images)

+ +These images were created by adding the following :doc:`dump image and dump_modify ` +commands to the ``in.ellipse.resquared`` input example: + +.. code-block:: LAMMPS + + # change + this + dump viz all image 1000 image-*.png type type ellipsoid type 3 4 0.05 & + size 600 600 zoom 2.2 shiny 0.1 fsaa yes view 80 -10 box yes 0.025 & + axes no 0.0 0.0 center s 0.5 0.5 0.5 ssao yes 32185474 0.6 + dump_modify viz pad 9 boxcolor white backcolor gray adiam 1 4 adiam 2 7 + +------------- + +Visualizing regions +------------------- + +Since there are several commands that operate on atoms within a specific +:doc:`region ` , it can be helpful to visualize the extent of +those regions, either for debugging the command settings or for +visualizing the production simulation. This is even more useful when +regions are (dynamically) translated or rotated or both with the +*rotate* or *move* keyword of the *region* command. The :doc:`dump +image region ` keyword can be used to enable such +visualizations. There are currently four different draw styles +available: + +* *filled*: a mesh of triangles is constructed for the surface of the + region and then drawn in the selected color. This draw style accounts + for the *open* keyword of the :doc:`region ` command and only + draws the faces of a region that are closed. +* *transparent*: this is the same visualization as draw style *filled*, + but in addition one can set the transparency of that visualization + through an opacity parameter in the range of 0.0 (invisible) to 1.0 + (fully opaque). +* *frame*: uses the same mesh of triangles as the two styles above + above, but renders the region's surface as a wireframe mesh with a + given cylinder diameter. +* *points*: generates a cloud of random points inside the simulation box + and then only draws a point as a sphere with the given color and + radius if it is located inside the region. This is useful to check + the impact of the *side in/out* setting of a region, and complementary + to the other three draw styles, which only show the region surfaces. + For visualization purposes any open faces of a region are ignored, + since a region with an open face matches *all* particles. + +It can sometimes be convenient to draw the same region with multiple +draw styles as can be seen from the example visualization images below. + +Notes on the visualization of individual region styles: + +* *plane*: since in theory planes extend infinitely, some form of + boundary has to be established for the visualization. Therefore a + mesh of triangles is constructed and transformed according to the + region settings and then only those triangles are drawn, where at + least one corner is inside the simulation box. For draw style + *points* the *side in/out* setting determines on which side of the + plane the points are drawn. +* *cone* and *cylinder*: use triangle meshes for draw styles *filled* + and *transparent* since there are no equivalent primitives (the + cylinder primitive only draws half of the cylinder, since it is + optimized for being capped). For draw style *frame*, only the rim + of the top or bottom is drawn and the side is represented by lines + from the top to the bottom. +* *box* and *prism*: for draw style *frame* only the edges of the region + are shown; for draw styles *filled* and *transparent*, the faces are + drawn instead, but only if they are not set as "open" in the + :doc:`region ` command. + +.. |region1| image:: img/region-plane.png + :width: 33% +.. |region2| image:: img/region-cones.png + :width: 33% +.. |region3| image:: img/region-boxes.png + :width: 33% + +|region1| |region2| |region3| + +.. raw:: html + +
(Region visualization examples. Click to see the full-size images)

+ + +Below is an example input deck for visualizing *cone* and *cylinder* regions: + +.. code-block:: LAMMPS + + region box block -2 2 -2 2 -2 2 + create_box 0 box + + variable rot equal PI*step/1000.0 + + region c1 cylinder x -1.0 0.0 0.5 -1.5 1.5 open 1 open 2 + region c2 cone y 1.0 -1.0 0.25 0.75 -1.5 1.5 open 3 + region c3 cylinder z 0.0 1.0 0.66 -0.1 1.5 open 1 open 2 rotate v_rot 0.3 0.0 0.3 0.0 1.0 1.0 + region c4 cone x -1.0 1.5 0.5 0.0 -1.0 2.0 + + dump viz all image 100 image-*.png type type size 600 600 zoom 1.4 shiny 0.1 view 70 20 & + box yes 0.025 axes no 0.0 0.0 fsaa yes ssao yes 314123 0.7 & + region c1 forestgreen transparent 0.5 & + region c2 goldenrod frame 0.05 & + region c2 cadetblue points 10000 0.15 & + region c2 goldenrod transparent 0.5 & + region c3 firebrick filled & + region c4 skyblue filled + dump_modify viz pad 4 boxcolor silver backcolor darkgray + + run 500 + +------------- + +Visualizing graphics provided by fix commands +--------------------------------------------- + +LAMMPS can display additional graphics objects in the :doc:`dump image +` output that are added by fix styles. These fall in two +categories: fixes that were written with the specific purpose of adding +graphics to the visualization and fixes that make objects or data +visible that they maintain internally. Examples for the latter case are +visualizing the indenter object from :doc:`fix indent ` or +the wall position from one of the wall fixes. The details of what kind +of graphics is added and how it can be configured is described in a +section titled **Dump image info** in the documentation of the +individual fix commands. + +Below is a table with links to the documentation of supported fix +styles: + +.. table_from_list:: + :columns: 4 + + * :doc:`fix graphics/arrows ` + * :doc:`fix graphics/isosurface ` + * :doc:`fix graphics/labels ` + * :doc:`fix graphics/objects ` + * :doc:`fix graphics/periodic ` + * :doc:`fix indent ` + * :doc:`fix reaxff/bonds ` + * :doc:`fix smd/wall_surface ` + * :doc:`fix wall/body/polygon ` + * :doc:`fix wall/body/polyhedron ` + * :doc:`fix wall/ees ` + * :doc:`fix wall/lj93 ` + * :doc:`fix wall/lj126 ` + * :doc:`fix wall/lj1043 ` + * :doc:`fix wall/colloid ` + * :doc:`fix wall/gran ` + * :doc:`fix wall/harmonic ` + * :doc:`fix wall/harmonic/outside ` + * :doc:`fix wall/lepton ` + * :doc:`fix wall/morse ` + * :doc:`fix wall/reflect ` + * :doc:`fix wall/reflect/stochastic ` + * :doc:`fix wall/srd ` + * :doc:`fix wall/table ` + +There is no support for :doc:`fix wall/region ` and +:doc:`fix wall/gran/region `, since regions can be +visualized with the *region* keyword of :doc:`dump image ` +(see discussion above). + +.. |fix1| image:: img/fix-graphics.png + :width: 19% +.. |fix2| image:: img/fix-arrows.png + :width: 19% +.. |fix3| image:: img/fix-dipoles.png + :width: 19% +.. |fix4| image:: img/fix-mesh.png + :width: 19% +.. |fix5| image:: img/fix-indent.png + :width: 19% + +|fix1| |fix2| |fix3| |fix4| |fix5| + +.. raw:: html + +
(Fix graphics visualization examples. Click to see the full-size images)

+ +Below are discussions about some aspects of specific fix commands and some input examples. + +------------- + +Fix graphics/objects +^^^^^^^^^^^^^^^^^^^^ + +Fix :doc:`graphics/objects ` adds some graphics +primitives and more complex objects like a progress bar to the +visualization where properties of the object(s) are controlled by +:doc:`equal-style or compatible variables `. + +Fix graphics/labels +^^^^^^^^^^^^^^^^^^^ + +Fix :doc:`graphics/labels ` adds graphics from +pixmaps to the visualization. These can be either images or text that +is rendered internally into a pixmap with a background and a frame. +Those pixmaps can be scaled, moved, made transparent, and updated during +the simulation and then integrated into the :doc:`dump image +` output. In both cases a "transparency" color can be +chosen to skip copying any pixels of that color to make parts of the +pixmap transparent. The text labels can contain :doc:`variables +` that will be expanded in the same was as with :doc:`fix +print ` so they can report values of computed properties. +Label positions are provided in the frame of reference of the simulation +box and thus they can obscure other objects in then visualization or be +obscured by objects. + +.. figure:: JPG/fix-graphics-labels-objects.png + :align: center + + Example image using graphics objects and labels that are updated during + the simulation. + +----------------------- + +Fix graphics/arrows +^^^^^^^^^^^^^^^^^^^ + +Fix :doc:`graphics/arrows ` adds per-atom or +per-chunk arrows to the visualization. The arrows represent some +per-atom property or per-chunk 3-vector property. For atoms, there are +the pre-defined properties *force*, *velocity*, and *dipole* (requires +atom style *dipole*). Everything else would have to be computed in +:doc:`atom-style or compatible variables ` and use the +*variable* keyword. For per-chunk properties, one needs to use the +*chunk* keyword and provide the IDs of three computes: a +:doc:`chunk/atom compute ` and two per-chunk +computes where the first defines the position of the (middle of the) +arrow and the second the direction and length of the arrow. Popular +choices would create per-molecule or per-bin chunks. Below is an +example input section that computes and displays both, the total dipole +moment (using :doc:`fix graphics/objects ` and the +per-molecule dipole moment as arrows in addition to the per-atom +velocities: + +.. code-block:: LAMMPS + + compute molchunk all chunk/atom molecule + compute cpos all com/chunk molchunk wrap yes + compute cdip all dipole/chunk molchunk + fix vel all graphics/arrows 10 velocity 50.0 0.066 autoscale 0.25 + fix vec all graphics/arrows 10 chunk molchunk cpos cdip 3 0.1 + + compute dip all dipole + variable scale equal 0.75 + variable dip1x equal -v_scale*c_dip[1] + variable dip1y equal -v_scale*c_dip[2] + variable dip1z equal -v_scale*c_dip[3] + variable dip2x equal v_scale*c_dip[1] + variable dip2y equal v_scale*c_dip[2] + variable dip2z equal v_scale*c_dip[3] + fix dipole all graphics/objects 1 arrow 1 v_dip1x v_dip1y v_dip1z v_dip2x v_dip2y v_dip2z 0.3 0.2 + + dump viz all image 100 image-*.png element type size 600 600 zoom 1.3 view 70 20 shiny 0.1 & + bond atom 0.2box yes 0.025 axes no 0.0 0.0 center s 0.5 0.5 0.5 fsaa yes & + fix dipole const 0 0 fix vec const 0 0 fix vel const 0 0 ssao yes 315465 0.8 + dump_modify viz pad 6 boxcolor white backcolor gray element O H bdiam 1 0.2 & + adiam 1 0.5 adiam 2 0.3 acolor 1 silver acolor 2 red fcolor vec goldenrod & + fcolor dipole forestgreen ftrans dipole 0.75 fcolor vel cyan ftrans vel 0.5 + +Fix graphics/isosurface +^^^^^^^^^^^^^^^^^^^^^^^ + +Fix :doc:`graphics/isosurface ` adds a +triangulated surface following a given isovalue through a 3d-grid of +data of some per-atom property. The data is spread out using a Gaussian +distribution with a given width and can be just a value of 1 (leading +to a grid representing the number density) or mass or some other computed +per-atom property from a :doc:`compute `, :doc:`fix `, or +atom-style :doc:`variable `. The isosurface can be represented +by either a wireframe or a mesh of triangles and there are five choices +for the resolution of the grid and thus the smoothness of the surface. + +The commands below provide an example for how to use the +*graphics/isosurface* fix to visualize the water and lipid bilayer of +the "rhodo" benchmark example as isosurfaces by using a green wireframe +and a transparent white triangle surface to represent those molecules. + +.. code-block:: LAMMPS + + group water type 4 33 + group membrane type 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 + group other subtract all water membrane + + fix water water graphics/isosurface 100 30.0 3.0 quality high property mass + fix membrane membrane graphics/isosurface 100 25.0 4.0 quality low property mass + + dump viz other image 100 image-*.png element type size 600 600 zoom 1.4641 view 80 10 & + shiny 0.2 fsaa yes bond atom type box yes 0.025 & + fix membrane const 2 0.33 fix water const 1 0.2 + dump_modify viz pad 9 boxcolor silver backcolor gray & + fcolor membrane darkgreen ftrans membrane 1.0 ftrans water 0.5 & + element H H H H H H H H H C C C C C C C C C C C C C N N N N N N N O O O O S S & + H H H H H C C C C C C N O O O P Cl Na H H H N C C C C C C C C C C C & + adiam 1 1.92 adiam 2 1.92 adiam 3 1.92 adiam 5 1.92 adiam 6 1.92 adiam 7 1.92 adiam 8 1.92 & + adiam 9 1.92 adiam 10 2.72 adiam 11 2.72 adiam 12 2.72 adiam 13 2.72 adiam 14 2.72 & + adiam 15 2.72 adiam 16 2.72 adiam 17 2.72 adiam 18 2.72 adiam 19 2.72 adiam 20 2.72 & + adiam 21 2.72 adiam 22 2.72 adiam 23 2.48 adiam 24 2.48 adiam 25 2.48 adiam 26 2.48 & + adiam 27 2.48 adiam 28 2.48 adiam 29 2.48 adiam 30 2.432 adiam 31 2.432 adiam 32 2.432 & + adiam 34 2.88 adiam 35 2.88 adiam 52 3.632 adiam 53 2.176 adiam 54 1.92 adiam 55 1.92 & + adiam 56 1.92 adiam 57 2.48 adiam 58 2.72 adiam 59 2.72 adiam 60 2.72 adiam 61 2.72 & + adiam 62 2.72 adiam 63 2.72 adiam 64 2.72 adiam 65 2.72 adiam 66 2.72 adiam 67 2.72 adiam 68 2.72 + +.. |isosurface1| image:: img/rhodo-all.png + :width: 49% +.. |isosurface2| image:: img/rhodo-iso.png + :width: 49% + +|isosurface1| |isosurface2| + +.. raw:: html + +
(Isosurface graphics visualization example. Click to see the full-size images)

+ +Fix reaxff/bonds +^^^^^^^^^^^^^^^^ + +Fix :doc:`reaxff/bonds ` of the :ref:`REAXFF package +` provides access to the list of bonds as they are +dynamically computed by the :doc:`ReaxFF pair style `. As +discussed above, this can be used to visualize bonds for a system where +there is no explicit bond topology defined. + +Fix smd/wall_surface +^^^^^^^^^^^^^^^^^^^^ + +Fix :doc:`smd/wall_surface ` of the :ref:`MACHDYN +package ` creates a custom wall from a mesh of triangles +that is read from an STL format file. diff --git a/doc/src/JPG/fix-graphics-example.png b/doc/src/JPG/fix-graphics-example.png index d9b1d4a5cbf..b62c9bd6fc2 100644 Binary files a/doc/src/JPG/fix-graphics-example.png and b/doc/src/JPG/fix-graphics-example.png differ diff --git a/doc/src/JPG/fix-graphics-labels-objects.png b/doc/src/JPG/fix-graphics-labels-objects.png new file mode 100644 index 00000000000..4eae1c90963 Binary files /dev/null and b/doc/src/JPG/fix-graphics-labels-objects.png differ diff --git a/doc/src/JPG/fix_graphics_labels_plot.png b/doc/src/JPG/fix_graphics_labels_plot.png new file mode 100644 index 00000000000..7f4d1bd9b94 Binary files /dev/null and b/doc/src/JPG/fix_graphics_labels_plot.png differ diff --git a/doc/src/JPG/lammps-gui-image.png b/doc/src/JPG/lammps-gui-image.png index 50cd8f02f3c..65fb8c31bd0 100644 Binary files a/doc/src/JPG/lammps-gui-image.png and b/doc/src/JPG/lammps-gui-image.png differ diff --git a/doc/src/JPG/lammps-gui-main.png b/doc/src/JPG/lammps-gui-main.png index c7a1e5a98cb..9c191b857aa 100644 Binary files a/doc/src/JPG/lammps-gui-main.png and b/doc/src/JPG/lammps-gui-main.png differ diff --git a/doc/src/JPG/lammps-gui-slideshow.png b/doc/src/JPG/lammps-gui-slideshow.png index 00f95e9c5ad..394a9c651db 100644 Binary files a/doc/src/JPG/lammps-gui-slideshow.png and b/doc/src/JPG/lammps-gui-slideshow.png differ diff --git a/doc/src/Modify_dump.rst b/doc/src/Modify_dump.rst index 1379326d1e4..bcb21c268e9 100644 --- a/doc/src/Modify_dump.rst +++ b/doc/src/Modify_dump.rst @@ -20,6 +20,10 @@ class. See dump.h for details. +---------------+---------------------------------------------------+ | write_data | write a proc's data to a file | +---------------+---------------------------------------------------+ +| modify_param | called when dump_modify is executed (optional) | ++---------------+---------------------------------------------------+ +| extract | provide access to internal data (optional) | ++---------------+---------------------------------------------------+ See the :doc:`dump ` command and its *custom* style for a list of keywords for atom information that can already be dumped by diff --git a/doc/src/Modify_fix.rst b/doc/src/Modify_fix.rst index fd089efa5db..b9d29667f17 100644 --- a/doc/src/Modify_fix.rst +++ b/doc/src/Modify_fix.rst @@ -129,7 +129,7 @@ derived class. See ``src/fix.h`` for additional details. +---------------------------+--------------------------------------------------------------------------------------------+ | thermo | compute quantities for thermodynamic output (optional) | +---------------------------+--------------------------------------------------------------------------------------------+ -| image | pass lists of graphics objects and their parameters to :doc:`dump image fix ` | +| image | pass lists of graphics objects to :doc:`dump image fix ` (optional) | +---------------------------+--------------------------------------------------------------------------------------------+ Typically, only a small fraction of these methods are defined for a diff --git a/doc/src/Packages.rst b/doc/src/Packages.rst index 47d4f8e2299..97205f0af31 100644 --- a/doc/src/Packages.rst +++ b/doc/src/Packages.rst @@ -233,6 +233,11 @@ whether an extra library is needed to build and use the package: - :doc:`Howto granular ` - pour - no + * - :ref:`GRAPHICS ` + - generate graphics output + - :doc:`Howto_viz` + - n/a + - no * - :ref:`H5MD ` - dump output via HDF5 - :doc:`dump h5md ` diff --git a/doc/src/Packages_details.rst b/doc/src/Packages_details.rst index 0da940565ce..3d5cb232e24 100644 --- a/doc/src/Packages_details.rst +++ b/doc/src/Packages_details.rst @@ -59,6 +59,7 @@ gives those details. * :ref:`EXTRA-PAIR ` * :ref:`FEP ` * :ref:`GPU ` + * :ref:`GRAPHICS ` * :ref:`GRANULAR ` * :ref:`H5MD ` * :ref:`INTEL ` @@ -1063,6 +1064,33 @@ This package has :ref:`specific installation instructions ` on the ---------- +.. _PKG-GRAPHICS: + +GRAPHICS package +---------------- + +**Contents:** + +Dump styles :doc:`image and movie `, supporting classes for +rendering images and fonts, several fixes for adding graphics objects to +visualizations, and the region2vmd command for exporting visualizations +of regions scripted graphics in VMD. + +**Supporting info:** + +* ``src/GRAPHICS``: filenames -> commands +* :doc:`Howto_viz` +* :doc:`dump image ` +* :doc:`dump movie ` +* :doc:`fix graphics/arrows ` +* :doc:`fix graphics/isosurface ` +* :doc:`fix graphics/labels `, +* :doc:`fix graphics/objects `, +* :doc:`fix graphics/periodic `, +* :doc:`region2vmd ` + +---------- + .. _PKG-GRANULAR: GRANULAR package diff --git a/doc/src/Run_formats.rst b/doc/src/Run_formats.rst index e2bd86f37d4..60b391a2c1f 100644 --- a/doc/src/Run_formats.rst +++ b/doc/src/Run_formats.rst @@ -6,6 +6,7 @@ This page provides a general overview of the kinds of files and file formats that LAMMPS is reading and writing. .. contents:: On this page + :local: :depth: 2 :backlinks: top diff --git a/doc/src/compute_com_chunk.rst b/doc/src/compute_com_chunk.rst index 4945774c85f..699dbcdecdd 100644 --- a/doc/src/compute_com_chunk.rst +++ b/doc/src/compute_com_chunk.rst @@ -8,11 +8,19 @@ Syntax .. code-block:: LAMMPS - compute ID group-ID com/chunk chunkID + compute ID group-ID com/chunk chunkID keyword args ... * ID, group-ID are documented in :doc:`compute ` command * com/chunk = style name of this compute command * chunkID = ID of :doc:`compute chunk/atom ` command +* zero or more keyword/value pairs may be appended +* keyword = *wrap* + + .. parsed-literal:: + + *wrap* logical = wrap center of mass back to cell if argument is *yes*, *true*, *on* or *1*, otherwise do not (default) + + Examples """""""" @@ -20,6 +28,7 @@ Examples .. code-block:: LAMMPS compute 1 fluid com/chunk molchunk + compute 1 all com/chunk molchunk wrap on Description """"""""""" @@ -27,35 +36,45 @@ Description Define a computation that calculates the center-of-mass for multiple chunks of atoms. -In LAMMPS, chunks are collections of atoms defined by a :doc:`compute chunk/atom ` command, which assigns each atom -to a single chunk (or no chunk). The ID for this command is specified -as chunkID. For example, a single chunk could be the atoms in a -molecule or atoms in a spatial bin. See the :doc:`compute chunk/atom ` and :doc:`Howto chunk ` -doc pages for details of how chunks can be defined and examples of how -they can be used to measure properties of a system. +In LAMMPS, chunks are collections of atoms defined by a :doc:`compute +chunk/atom ` command, which assigns each atom to a +single chunk (or no chunk). The ID for this command is specified as +chunkID. For example, a single chunk could be the atoms in a molecule +or atoms in a spatial bin. See the :doc:`compute chunk/atom +` and :doc:`Howto chunk ` doc pages for +details of how chunks can be defined and examples of how they can be +used to measure properties of a system. -This compute calculates the :math:`(x,y,z)` coordinates of the center of mass -for each chunk, which includes all effects due to atoms passing through -periodic boundaries. +This compute calculates the :math:`(x,y,z)` coordinates of the center of +mass for each chunk, which includes all effects due to atoms passing +through periodic boundaries. + +.. versionadded:: TBD + +If the *wrap* flag is used with an argument of *yes*, *true*, *on* or +*1* the computed center of mass is wrapped back into the simulation +cell. With an argument of *no*, *false*, *off* or 0 it is not (the +default). Note that only atoms in the specified group contribute to the calculation. The :doc:`compute chunk/atom ` command -defines its own group; atoms will have a chunk ID = 0 if they are not -in that group, signifying they are not assigned to a chunk, and will -thus also not contribute to this calculation. You can specify the -"all" group for this command if you simply want to include atoms with -non-zero chunk IDs. +defines its own group; atoms will have a chunk ID = 0 if they are not in +that group, signifying they are not assigned to a chunk, and will thus +also not contribute to this calculation. You can specify the "all" +group for this command if you simply want to include atoms with non-zero +chunk IDs. .. note:: - The coordinates of an atom contribute to the chunk's - center-of-mass in "unwrapped" form, by using the image flags - associated with each atom. See the :doc:`dump custom ` command - for a discussion of "unwrapped" coordinates. See the Atoms section of - the :doc:`read_data ` command for a discussion of image flags + The coordinates of an atom contribute to the chunk's center-of-mass + in "unwrapped" form, by using the image flags associated with each + atom. See the :doc:`dump custom ` command for a discussion of + "unwrapped" coordinates. See the Atoms section of the + :doc:`read_data ` command for a discussion of image flags and how they are set for each atom. You can reset the image flags (e.g., to 0) before invoking this compute by using the - :doc:`set image ` command. + :doc:`reset_atoms image ` or :doc:`set image ` + commands. The simplest way to output the results of the compute com/chunk calculation to a file is to use the :doc:`fix ave/time ` @@ -71,14 +90,15 @@ Output info """"""""""" This compute calculates a global array where the number of rows = the -number of chunks *Nchunk* as calculated by the specified :doc:`compute chunk/atom ` command. The number of columns is -3 for the :math:`(x,y,z)` center-of-mass coordinates of each chunk. These -values can be accessed by any command that uses global array values -from a compute as input. See the :doc:`Howto output ` doc -page for an overview of LAMMPS output options. +number of chunks *Nchunk* as calculated by the specified :doc:`compute +chunk/atom ` command. The number of columns is 3 +for the :math:`(x,y,z)` center-of-mass coordinates of each chunk. These +values can be accessed by any command that uses global array values from +a compute as input. See the :doc:`Howto output ` doc page +for an overview of LAMMPS output options. -The array values are "intensive". The array values will be in -distance :doc:`units `. +The array values are "intensive". The array values will be in distance +:doc:`units `. Restrictions """""""""""" @@ -92,4 +112,4 @@ Related commands Default """"""" -none +wrap = off diff --git a/doc/src/dump.rst b/doc/src/dump.rst index 13667180b32..6f0c511cf05 100644 --- a/doc/src/dump.rst +++ b/doc/src/dump.rst @@ -1030,6 +1030,9 @@ To write compressed dump files, you must either compile LAMMPS with the ``-DLAMMPS_GZIP`` option or use the styles from the COMPRESS package. See the :doc:`Build settings ` page for details. +To create images or movies, you must install the GRAPHICS package. +See the :doc:`Build extras ` page for details. + While a dump command is active (i.e., has not been stopped by using the :doc:`undump command `), no commands may be used that will change the timestep (e.g., :doc:`reset_timestep `). diff --git a/doc/src/dump_image.rst b/doc/src/dump_image.rst index ed77daef314..1d0ff5b6b11 100644 --- a/doc/src/dump_image.rst +++ b/doc/src/dump_image.rst @@ -24,7 +24,7 @@ Syntax * color = atom attribute that determines color of each atom * diameter = atom attribute that determines size of each atom * zero or more keyword/value pairs may be appended -* keyword = *atom* or *adiam* or *autobond* or *bond* or *grid* or *line* or *tri* or *body* or *fix* or *size* or *view* or *center* or *up* or *zoom* or *box* or *axes* or *region* or *subbox* or *shiny* or *fsaa* or *ssao* +* keyword = *atom* or *adiam* or *autobond* or *bond* or *grid* or *line* or *tri* or *ellipsoid* or *body* or *fix* or *size* or *view* or *center* or *up* or *zoom* or *box* or *axes* or *region* or *subbox* or *shiny* or *fsaa* or *ssao* .. parsed-literal:: @@ -49,9 +49,14 @@ Syntax *tri* = color tflag width color = *type* tflag = 1 for just triangle, 2 for just tri edges, 3 for both - width = numeric value for tringle edge width (distance units) - *body* = color bflag1 bflag2 + width = numeric value for triangle edge width (distance units) + *ellipsoid* = color eflag level width color = *type* + eflag = 1 for triangles, 2 for wireframe, 3 for both + level = mesh refinement level, value between 1 (low resolution) and 6 (ultra high resolution) + width = diameter of wireframe edges (distance units) (ignored for triangles) + *body* = color bflag1 bflag2 + color = *type* or *index* bflag1,bflag2 = 2 numeric flags to affect how bodies are drawn *fix* = fixID color fflag1 fflag2 fixID = ID of fix that generates objects to draw @@ -117,7 +122,7 @@ Syntax dump_modify dump-ID keyword values ... * these keywords apply only to the *image* and *movie* styles and are documented on this page -* keyword = *acolor* or *adiam* or *amap* or *gmap* or *atrans* or *backcolor* or *bcolor* or *bdiam* or *btrans* or *bitrate* or *boxcolor* or *color* or *framerate* or *axestrans* or *boxtrans* or *subboxtrans* +* keyword = *acolor* or *adiam* or *amap* or *gmap* or *atrans* or *backcolor* or *backcolor2* or *bcolor* or *bdiam* or *btrans* or *bitrate* or *boxcolor* or *color* or *framerate* or *axestrans* or *boxtrans* or *subboxtrans* * see the :doc:`dump modify ` doc page for more general keywords .. parsed-literal:: @@ -153,6 +158,8 @@ Syntax transparency = transparency of atoms of that type (value between 0 (invisible) and 1 (fully opaque)) *backcolor* arg = color color = name of color for background + *backcolor2* arg = color + color = name of second color for vertical background gradiant. "none" to disable gradient *bcolor* args = type color type = bond type (numeric or type label) or range of numeric types (see below) color = name of color or color1/color2/... @@ -176,6 +183,9 @@ Syntax *fcolor* args = fix-ID color fix-ID = ID of the fix color = name of color for image objects provided by this fix + *ftrans* args = fix-ID transparency + fix-ID = ID of the fix + transparency = transparency for image objects provided by this fix when using "const" color style *bitrate* arg = rate rate = target bitrate for movie in kbps *framerate* arg = fps @@ -219,18 +229,27 @@ has been run, using the :doc:`rerun ` command to read snapshots from an existing dump file, and using these dump commands in the rerun script to generate the images/movie. -Here are two sample images, rendered as :math:`1024\times 1024` JPEG files. - .. |dump1| image:: img/dump1.jpg - :width: 48% -.. |dump2| image:: img/dump2.jpg - :width: 48% + :width: 19% +.. |dump2| image:: img/dump2.png + :width: 19% +.. |dump3| image:: img/dump3.png + :width: 19% +.. |dump4| image:: img/dump4.png + :width: 19% +.. |dump5| image:: img/dump5.png + :width: 21.3% + +Here are five sample images, rendered as JPEG or PNG files. -|dump1| |dump2| +|dump1| |dump2| |dump4| |dump5| |dump3| .. raw:: html - Click to see the full-size images: +
(Click to see the full-size images)
+ +A detailed discussion of advanced graphics settings and workflows +with examples is provided in the :doc:`Howto_viz` howto. Only atoms in the specified group are rendered in the image. The :doc:`dump_modify region and thresh ` commands can also @@ -260,7 +279,7 @@ described below. To write out JPEG and PNG format files, you must build LAMMPS with support for the corresponding JPEG or PNG library. To convert images -into movies, LAMMPS has to be compiled with the -DLAMMPS_FFMPEG +into movies, LAMMPS has to be compiled with the ``-DLAMMPS_FFMPEG`` flag. See the :doc:`Build settings ` page for details. @@ -370,7 +389,7 @@ The *atom* keyword allow you to turn off the drawing of all atoms, if the specified value is *no*\ . Note that this will not turn off the drawing of particles that are represented as lines, triangles, or bodies, as discussed below. These particles can be drawn separately -if the *line*, *tri*, or *body* keywords are used. +if the *line*, *tri*, *ellipsoid*, or *body* keywords are used. The *adiam* keyword allows you to override the *diameter* setting to set a single numeric *size*\ . All atoms will be drawn with that @@ -401,13 +420,13 @@ through using :doc:`fix reaxff/bonds ` with the ---------- The *bond* keyword allows to you to alter how bonds are drawn. A bond -is only drawn if both atoms in the bond are being drawn due to being -in the specified group and due to other selection criteria -(e.g. region, threshold settings of the -:doc:`dump_modify ` command). By default, bonds are drawn -if they are defined in the input data file as read by the -:doc:`read_data ` command. Using *none* for both the bond -*color* and *width* value will turn off the drawing of all bonds. +is only drawn if both atoms in the bond are being drawn due to being in +the specified group and due to other selection criteria (e.g. region, +threshold settings of the :doc:`dump_modify ` command). By +default, bonds are drawn if they are defined in the input data file as +read by the :doc:`read_data ` command. Using *none* for both +the bond *color* and *width* value will turn off the drawing of all +bonds. If *atom* is specified for the bond *color* value, then each bond is drawn in 2 halves, with the color of each half being the color of the @@ -487,8 +506,51 @@ default the mapping of types to colors is as follows: * type 5 = aqua * type 6 = cyan -and repeats itself for types > 6. There is not yet an option to -change this via the dump_modify command. +and repeats itself for types > 6. + +---------- + +.. versionadded:: TBD + +The *ellipsoid* keyword can be used when :doc:`atom_style ellipsoid +` is used to define particles as ellipsoids, and will draw +them as a mesh of triangles or edges or both, depending on the setting +for *eflag*\ . If edges are drawn, the *width* setting determines the +diameters of the line segments. If this keyword is not used, ellipsoid +particles will be drawn as spheres, the same as if they were regular +atoms. The only setting currently allowed for the *color* value is +*type*, which will color the triangles according to the atom type of the +particle. By default the mapping of types to colors is as follows: + +* type 1 = red +* type 2 = green +* type 3 = blue +* type 4 = yellow +* type 5 = aqua +* type 6 = cyan + +and repeats itself for types > 6. + +The *level* setting determines the number of triangles in the mesh of +triangles and thus the resolution of the representation of the +ellipsoid. At level 1 the ellipsoid is represented by an octahedron +that is stretched according to the ellipsoid's shape parameters. For +each higher level, any of the triangles is replaced by four triangles +and their edges are shifted to be on the surface of the ellipsoid. The +maximum allowed level is 6 (corresponding to 8192 triangles). + +.. admonition:: Image quality versus rendering speed + :class: Hint + + Since the rendered ellipsoids are constructed from iteratively + refined triangle meshes, the image quality increases with each + refinement level, but so does the computational effort to render the + image. Rendering only triangles is much faster than rendering the + wireframe edges. However, at mesh refinement levels of 4 and up, + artifacts from the image rendering library are more common where + triangles meet. These artifacts can be somewhat hidden by using the + *fsaa yes* setting, but are also less visible when rendering both + edges and triangles. ---------- @@ -509,9 +571,14 @@ passed to the body style to affect how the drawing of a body particle is done. See the :doc:`Howto body ` page for a description of what these parameters mean for each body style. -The only setting currently allowed for the *color* value is *type*, -which will color the body particles according to the atom type of the -particle. By default the mapping of types to colors is as follows: +.. versionchanged:: TDB + +The there are currently two supported settings for the *color* value: +*type*, or *index*. With the *type* setting the body particles will be +colored according to the atom type of the particle. With the *index* +setting the coloring follows the body index instead. For both settings, +the value (type or index) is mapped to the colors of atom types. The +list of colors is by default as follows: * type 1 = red * type 2 = green @@ -520,8 +587,11 @@ particle. By default the mapping of types to colors is as follows: * type 5 = aqua * type 6 = cyan -and repeats itself for types > 6. There is not yet an option to -change this via the dump_modify command. +and repeats itself for types > 6. This list can by changed with the +:doc:`dump_modify acolor ` command. If more different +colors than atom types are desired, the number of atom types must be +increased when using either the :doc:`create_box ` or the +:doc:`read_data ` command. ---------- @@ -530,25 +600,7 @@ change this via the dump_modify command. Support for several fix styles added and more flexible color selection The *fix* keyword can be used with a :doc:`fix ` that produces -objects to be drawn. Below is a list of supported fixes: - -* :doc:`fix graphics ` -* :doc:`fix indent ` -* :doc:`fix smd/wall_surface ` -* :doc:`fix wall/lj93 ` -* :doc:`fix wall/lj126 ` -* :doc:`fix wall/lj1043 ` -* :doc:`fix wall/colloid ` -* :doc:`fix wall/gran ` -* :doc:`fix wall/harmonic ` -* :doc:`fix wall/harmonic/outside ` -* :doc:`fix wall/lepton ` -* :doc:`fix wall/morse ` -* :doc:`fix wall/reflect ` -* :doc:`fix wall/reflect/stochastic ` -* :doc:`fix wall/table ` - -The fix keyword may be used multiple times to include visualizations of +objects to be drawn. The fix keyword may be used multiple times to include visualizations of graphics objects from multiple fixes. The fix keyword is followed by the :doc:`fix ID ` of the fix, the color style setting and two numerical values *fflag1* and *fflag2*. @@ -567,35 +619,45 @@ the fix is done. See the documentation of the individual fixes for a description of what these parameters mean for the graphics objects provided by those fixes. +More details and some examples for including graphics objects from fix +commands are in the :doc:`Howto_viz` howto. + ---------- .. versionadded:: 10Sep2025 .. versionchanged:: TBD - style *transparency* was added + draw style *transparency* was added The *region* keyword can be used to create a graphical representation of a :doc:`region `. This can be helpful in debugging the location and extent of regions, especially when those have parameters controlled -by variables. Three styles of representing a region are available: -*filled*\, *transparency*\, *frame*\, and *points*. With style *filled* -the surface of the region is triangulated and drawn. For region styles -that support open faces, surfaces for such open faces are skipped. The -style *transparent* is like *filled* but takes an additional parameter -in the range of 0.0 to 1.0 that defines the opacity and thus allows to -see what is inside the region. Draw style *frame* represents the region -with a mesh of "wires". The diameter of these "wires" can be set. -Unlike with the *filled* style and similar to the *transparent* style, -you can see what is *inside* the region with this draw style. The third -draw style, *points*\, generates a random point cloud inside the -simulation box and draws only those points that are within the region. -Draw styles *filled*\, *transparent*\, and *frame* support only -"primitive" region styles (no unions or intersections), but the *points* -draw style supports *all* region styles. - -Recommended transparency settings are the values of 0.25, 0.5, or 0.75 -when used in combination with *fsaa on*. +by variables. The sequence of arguments to the *region* are: the +region-ID, the color for drawing the region, the draw style, and +possible additional arguments as required by the draw style. + +Four draw styles of representing a region are available: *filled*\, +*transparency*\, *frame*\, and *points*. With draw style *filled* the +surface of the region is triangulated and drawn. For region styles that +support open faces, surfaces for such open faces are skipped. The style +*transparent* is like *filled* but takes an additional parameter in the +range of 0.0 to 1.0 that defines the opacity and thus allows to see what +is inside the region for values < 1. Draw style *frame* represents the +region with a mesh of "wires". The diameter of these "wires" are set +with the following argument. Unlike with the *filled* style and similar +to the *transparent* style, you can see what is *inside* the region with +this draw style. The fourth draw style, *points*\, generates a random +point cloud inside the simulation box and draws only those points that +are within the region. This uses the same test than what is used to +determine if an atom is inside the region but ignores any open faces +(which would match *all* positions as "inside"). Draw styles *filled*\, +*transparent*\, and *frame* support only "primitive" region styles (no +unions or intersections of multiple regions), but the *points* draw +style supports *all* region styles. + +Recommended transparency values are 0.25, 0.5, or 0.75 when used in +combination with *fsaa on*. ---------- @@ -608,15 +670,14 @@ The *view*, *center*, *up*, and *zoom* values determine how 3d simulation space is mapped to the 2d plane of the image. Basically they control how the simulation box appears in the image. -All of the *view*, *center*, *up*, and *zoom* values can be -specified as numeric quantities, whose meaning is explained below. -Any of them can also be specified as an :doc:`equal-style variable `, -by using v_name as the value, where "name" is -the variable name. In this case the variable will be evaluated on the -timestep each image is created to create a new value. If the -equal-style variable is time-dependent, this is a means of changing -the way the simulation box appears from image to image, effectively -doing a pan or fly-by view of your simulation. +All of the *view*, *center*, *up*, and *zoom* values can be specified as +numeric quantities, whose meaning is explained below. Any of them can +also be specified as an :doc:`equal-style variable `, by using +v_name as the value, where "name" is the variable name. In this case +the variable will be evaluated on the timestep each image is created to +create a new value. If the equal-style variable is time-dependent, this +is a means of changing the way the simulation box appears from image to +image, effectively doing a pan or fly-by view of your simulation. The *view* keyword determines the viewpoint from which the simulation box is viewed, looking towards the *center* point. The *theta* value @@ -717,110 +778,9 @@ parameter. If *no* is set, no depth shading is performed. The calculation of this effect can increase the cost of computing the image substantially by 5x or more, especially with larger images. When used in combination with the *fsaa* keyword the computational cost of depth -shading is particularly large. - ----------- - -Image Quality Settings -"""""""""""""""""""""" - -The two keywords *fsaa* and *ssao* can be used to improve the image -quality at the expense of additional computational cost to render the -images. The images below show from left to right the same render with -default settings, with *fsaa* added, with *ssao* added, and with both -keywords added. - -.. |imagequality1| image:: JPG/image.default.png - :width: 24% -.. |imagequality2| image:: JPG/image.fsaa.png - :width: 24% -.. |imagequality3| image:: JPG/image.ssao.png - :width: 24% -.. |imagequality4| image:: JPG/image.both.png - :width: 24% - -|imagequality1| |imagequality2| |imagequality3| |imagequality4| - ----------- - -A series of JPEG, PNG, or PPM images can be converted into a movie -file and then played as a movie using commonly available tools. Using -dump style *movie* automates this step and avoids the intermediate -step of writing (many) image snapshot file. But LAMMPS has to be -compiled with -DLAMMPS_FFMPEG and an FFmpeg executable have to be -installed. - -To manually convert JPEG, PNG or PPM files into an animated GIF or -MPEG or other movie file you can use: - -* a) Use the ImageMagick convert program. - - .. code-block:: bash - - convert *.jpg foo.gif - convert -loop 1 *.ppm foo.mpg - - Animated GIF files from ImageMagick are not optimized. You can use - a program like gifsicle to optimize and thus massively shrink them. - MPEG files created by ImageMagick are in MPEG-1 format with a rather - inefficient compression and low quality compared to more modern - compression styles like MPEG-4, H.264, VP8, VP9, H.265 and so on. - -* b) Use QuickTime. - - Select "Open Image Sequence" under the File menu Load the images into - QuickTime to animate them Select "Export" under the File menu Save the - movie as a QuickTime movie (\*.mov) or in another format. QuickTime - can generate very high quality and efficiently compressed movie - files. Some of the supported formats require to buy a license and some - are not readable on all platforms until specific runtime libraries are - installed. - -* c) Use FFmpeg - - FFmpeg is a command-line tool that is available on many platforms and - allows extremely flexible encoding and decoding of movies. - - .. code-block:: bash - - cat snap.*.jpg | ffmpeg -y -f image2pipe -c:v mjpeg -i - -b:v 2000k movie.m4v - cat snap.*.ppm | ffmpeg -y -f image2pipe -c:v ppm -i - -b:v 2400k movie.avi - - Front ends for FFmpeg exist for multiple platforms. For more - information see the `FFmpeg homepage `_ - ----------- - -Play the movie: - -* a) Use your browser to view an animated GIF movie. - - Select "Open File" under the File menu - Load the animated GIF file - -* b) Use the freely available mplayer or ffplay tool to view a - movie. Both are available for multiple OSes and support a large - variety of file formats and decoders. - - .. code-block:: bash - - mplayer foo.mpg - ffplay bar.avi - -* c) Use the `Pizza.py `_ - `animate tool `_, - which works directly on a series of image files. - - .. code-block:: python - - a = animate("foo*.jpg") - -* d) QuickTime and other Windows- or macOS-based media players can - obviously play movie files directly. Similarly for corresponding tools - bundled with Linux desktop environments. However, due to licensing - issues with some file formats, the formats may require installing - additional libraries, purchasing a license, or may not be - supported. +shading is particularly large. In case LAMMPS has been :doc:`compiled +with OpenMP support `, the SSAO processing is distributed +across multiple threads. ---------- @@ -1003,6 +963,17 @@ The *backcolor* sets the background color of the images. The color name can be any of the 140 pre-defined colors (see below) or a color name defined by the dump_modify color option. +.. versionadded:: TBD + +The *backcolor2* sets a second background color of the images to create +a vertical background gradient. The regular background color is the +color at the bottom and *backcolor2* sets the background color at the +top. The color in between is a linear interpolation between those two +colors. The color name can be any of the 140 pre-defined colors (see +below) or a color name defined by the dump_modify color option. Using a +color name of "none" will disable the background gradient feature (this +is the default). + ---------- The *bcolor* keyword can be used with the dump image command, with its @@ -1095,18 +1066,27 @@ written to the image. This can be controlled with various must be between 0.0 (invisible) and 1.0 (fully opaque). The default setting for all is 1.0. -Recommended transparency settings are the values of 0.25, 0.5, or 0.75 -when used in combination with *fsaa on*. +Recommended transparency values are 0.25, 0.5, or 0.75 when used in +combination with *fsaa on*. ---------- .. versionadded:: TBD The *fcolor* keyword sets the color of any image objects created by a -fix. The first argument is the fix ID used with the *dump image fix* -command and the second argument is the color name. The color name can -be any of the 140 pre-defined colors (see below) or a color name defined -by the *dump_modify color* option. +fix when using the color style "const". The first argument is the fix ID +used with the *dump image fix* command and the second argument is the +color name. The color name can be any of the 140 pre-defined colors +(see below) or a color name defined by the *dump_modify color* option. + +The *ftrans* keyword sets the transparency of any image objects created +by a fix when using the color style "const". The first argument is the +fix ID used with the *dump image fix* command and the second argument is +the transparency value. The transparency value must be between 0.0 +(invisible) and 1.0 (fully opaque). The default setting is 1.0. + +Recommended transparency values are 0.25, 0.5, or 0.75 when used in +combination with *fsaa on*. ---------- @@ -1138,18 +1118,20 @@ The arguments for the *gmap* keyword are identical to those for the Restrictions """""""""""" -To write JPEG images, you must use the -DLAMMPS_JPEG switch when -building LAMMPS and link with a JPEG library. To write PNG images, you -must use the -DLAMMPS_PNG switch when building LAMMPS and link with a -PNG library. +The *dump image* and *dump movie* commands are part of the GRAPHICS +package. They are only enabled if LAMMPS was built with that package. +See the :doc:`Build package ` page for more info. + +To write JPEG or PNG format images, support for the corresponding +graphics libraries must have been compiled and linked into LAMMPS. +Please see the :ref:`instructions for building LAMMPS with the +GRAPHICS package ` for more information on how to do that. To write *movie* dumps, you must use the -DLAMMPS_FFMPEG switch when building LAMMPS and have the FFmpeg executable available on the machine where LAMMPS is being run. Typically its name is lowercase (i.e., "ffmpeg"). -See the :doc:`Build settings ` page for details. - Note that since FFmpeg is run as an external program via a pipe, LAMMPS has limited control over its execution and no knowledge about errors and warnings printed by it. Those warnings and error messages @@ -1180,7 +1162,12 @@ FFmpeg and which does not have this limitation (e.g., .avi, .mkv, mp4). Related commands """""""""""""""" -:doc:`dump `, :doc:`dump_modify `, :doc:`undump ` +:doc:`dump `, :doc:`dump_modify `, :doc:`undump `, +:doc:`fix graphics/arrows `, +:doc:`fix graphics/isosurface `, +:doc:`fix graphics/labels `, +:doc:`fix graphics/objects `, +:doc:`fix graphics/periodic ` Default """"""" @@ -1214,6 +1201,7 @@ The defaults for the dump_modify keywords specific to dump image and dump movie * amap = min max cf 0.0 2 min blue max red * atrans = 1.0 * backcolor = black +* backcolor2 = none * bcolor = \* red/green/blue/yellow/aqua/cyan * bdiam = \* 0.5 * btrans = 1.0 diff --git a/doc/src/fix.rst b/doc/src/fix.rst index 2a71968ba1e..cd518e54f68 100644 --- a/doc/src/fix.rst +++ b/doc/src/fix.rst @@ -262,7 +262,11 @@ accelerated styles exist. * :doc:`gjf ` - statistically correct Langevin temperature control using the GJ methods * :doc:`gld ` - generalized Langevin dynamics integrator * :doc:`gle ` - generalized Langevin equation thermostat -* :doc:`graphics ` - add graphics objects to :doc:`dump image ` output +* :doc:`graphics/arrows ` - add arrow graphics objects to :doc:`dump image ` output +* :doc:`graphics/isosurface ` - add an isosurface for a group of atoms to :doc:`dump image ` output +* :doc:`graphics/labels ` - add images or text as graphics objects to :doc:`dump image ` output +* :doc:`graphics/objects ` - add graphics objects to :doc:`dump image ` output +* :doc:`graphics/periodic ` - add selected periodic images of atoms and bonds to :doc:`dump image ` output * :doc:`gravity ` - add gravity to atoms in a granular simulation * :doc:`grem ` - implements the generalized replica exchange method * :doc:`halt ` - terminate a dynamics run or minimization diff --git a/doc/src/fix_electron_stopping.rst b/doc/src/fix_electron_stopping.rst index 1604c999425..00b61fb8d9c 100644 --- a/doc/src/fix_electron_stopping.rst +++ b/doc/src/fix_electron_stopping.rst @@ -218,7 +218,7 @@ The default is no limitation by region, and minneigh = 1. .. _CasP: -**(CasP)** CasP webpage: http://www.casp-program.org/ +**(CasP)** CasP webpage: https://lief.if.ufrgs.br/pub/CasP/ .. _Stewart2018: diff --git a/doc/src/fix_graphics_arrows.rst b/doc/src/fix_graphics_arrows.rst new file mode 100644 index 00000000000..3c2dc161cac --- /dev/null +++ b/doc/src/fix_graphics_arrows.rst @@ -0,0 +1,196 @@ +.. index:: fix graphics/arrows + +fix graphics/arrows command +=========================== + +Syntax +"""""" + +.. code-block:: LAMMPS + + fix ID group-ID graphics/arrows Nevery mode keyword args ... + +* ID, group-ID are documented in :doc:`fix ` command +* graphics/arrows = style name of this fix command +* Nevery = update graphics information every this many time steps +* mode = one of the following modes *dipole* or *force* or *velocity* or *variable* or *chunk* + + .. parsed-literal:: + + *dipole* args = scale radius + scale = scale factor for the dipole moment to determine the arrow length + radius = radius for arrows (length units) + *force* args = scale radius + scale = scale factor for the force vector to determine the arrow length + radius = radius for arrows (length units) + *velocity* args = scale radius + scale = scale factor for the velocity vector to determine the arrow length + radius = radius for arrows (length units) + *variable* args = xval yval zval radius + xval = x value for arrow vector (may be a variable) + yval = y value for arrow vector (may be a variable) + zval = z value for arrow vector (may be a variable) + radius = radius for arrows (length units) + *chunk* args = chunk-ID pos-ID vec-ID scale radius + chunk-ID = ID of :doc:`compute chunk/atom ` command + pos-ID = ID of a per-chunk compute that computes the positions for the arrows + vec-ID = ID of a per-chunk compute that computes the arrow vectors + scale = scale factor for the per-chunk vector to determine the arrow length + radius = radius for arrows (length units) + +* zero or more keyword/value pairs may be appended +* keyword = *autoscale* + + .. parsed-literal:: + + *autoscale* value = automatically scale arrows so they have an average length of "value" + +Examples +"""""""" + +.. code-block:: LAMMPS + + fix vec all graphics/arrows 10 velocity 20.0 0.066 autoscale 0.5 + fix vec all graphics/arrows 100 variable v_xnorm v_znorm 0.0 0.066 + fix vec all graphics/arrows 100 chunk molchunk com dip 1.0 0.05 + +Description +""""""""""" + +.. versionadded:: TBD + +This fix allows to add arrows to images rendered with :doc:`dump image +` using the *fix* keyword to represent vector properties +with arrows for either all atoms in the fix group or for :doc:`chunks +`. + +The *group-ID* sets the group ID of the atoms selected to have the selected +property represented. This may be a dynamic group. + +The *Nevery* keyword determines how often the arrows graphics data is +updated. This should be the same value as the corresponding *N* +parameter of the :doc:`dump ` image command. LAMMPS will stop +with an error message if the settings for this fix and the dump command +are not compatible. + +There are five keywords available that determine what is shown: *dipole* +will show the per-atom dipole vector, *force* the per-atom force, +*velocity* the per-atom velocity, *variable* a custom vector constructed +from three constants or atom- or equal-style variables. With the *chunk* +keyword the arrows shown will represent per-chunk vector data. + +The *xval*\ , *yval*\ , and *zval*\ , arguments to the *variable* mode +define a custom vector that can be composed of numbers or :doc:`atom- or +equal-style variables `. If any of these values is a +variable, it should be specified as *v_name*\ , where "name" is the +variable name. In this case, the variable will be evaluated each +timestep, and its value used to define the arrow for each atom. Since +variables can reference :doc:`computes `, :doc:`fixes `, +:doc:`custom per-atom properties `, and other +variables, this can be used to construct arrows for almost any per-atom +property available in LAMMPS. + +The *chunk-ID* is the ID of a :doc:`compute chunk/atom +` command. In LAMMPS, chunks are collections of +atoms and there are per-chunk computes that compute properties for them. +See the :doc:`compute chunk/atom ` and :doc:`Howto +chunk ` pages for details of how chunks can be defined and +examples of how they can be used to measure properties of a system. + +The *pos-ID* is the ID of a per-chunk :doc:`compute command `. +Most commonly this will be either :doc:`compute com/chunk +` for "mobile" chunks or compute :doc:`compute +property/chunk ` for binning based chunks. The +*vec-ID* is the ID of a per-chunk :doc:`compute command `. +Either per-chunk compute must return a global array with at least 3 +columns and *only* the first three columns are used for the arrows. For +computes that compute a tensor only the trace of the tensor is used. +Currently the following computes are compatible: + + * :doc:`angmom/chunk ` + * :doc:`com/chunk ` + * :doc:`dipole/chunk ` + * :doc:`dipole/tip4p/chunk ` + * :doc:`gyration/chunk ` (with optional *tensor* keyword) + * :doc:`gyration/shape/chunk ` + * :doc:`inertia/chunk ` + * :doc:`msd/chunk ` + * :doc:`omega/chunk ` + * :doc:`property/chunk ` (with arguments *coord1* *coord2* *coord3*) + * :doc:`reduce/chunk ` (with three or more properties) + * :doc:`torque/chunk ` + * :doc:`vacf/chunk ` + * :doc:`vcm/chunk ` + +The *scale* quantity determines the length of the arrows. It should be +chosen so that when multiplied with the per-atom vector quantity the result +is of the same order of magnitude as atom positions, so that the vectors +can be seen well. + +The *radius* quantity determines the width of the arrows. + +The optional *autoscale* keyword allows to dynamically determine the +*scale* quantity so that the average length of the arrows is set to the +value of the keyword's argument. The computed scale factor can be +accessed by various :doc:`output commands ` as a global +scalar (see below). + +----------- + +Dump image info +""""""""""""""" + +.. versionadded:: TBD + +Fix graphics/arrows is designed to be used with the *fix* keyword of +:doc:`dump image `. The fix will add arrows based on the +atoms in the fix group or based on chunks to *dump image* so that they +are included in the rendered image. + +The color of the arrows is by default that of the atoms when using color +styles "type" or "element". With color style "const" the default value +of "white" can be changed using :doc:`dump_modify fcolor `. +The transparency is by default fully opaque and can be changed with +*dump\_modify ftrans*\ . + +The *fflag1* and *fflag2* settings of *dump image fix* are currently ignored. + +Restart, fix_modify, output, run start/stop, minimize info +========================================================== + +No information about this fix is written to :doc:`binary restart files +`. + +None of the :doc:`fix_modify ` options apply to this fix. + +This fix computes a global scalar representing the current scale factor +for displaying the arrows, which can be accessed by various +:doc:`output commands `. This is the *autoscale* +keyword argument value divided by the average length of the selected +vector property. If the *autoscale* keyword is not used, it is the +scale value set by the *fix graphics/arrows* command or 1.0. +The scalar value calculated by this fix is "intensive". + +Restrictions +"""""""""""" + +This fix is part of the GRAPHICS package. It is only enabled if LAMMPS +was built with that package. See the :doc:`Build package +` page for more info. + +The *dipole* mode requires the use of :doc:`atom style dipole +` or a hybrid atom style that includes it. + +Related commands +"""""""""""""""" + +:doc:`fix graphics/labels `, +:doc:`fix graphics/isosurface `, +:doc:`fix graphics/objects `, +:doc:`fix graphics/periodic `, +:doc:`fix graphics/objects ` + +Default +""""""" + +autoscale is off by default diff --git a/doc/src/fix_graphics_isosurface.rst b/doc/src/fix_graphics_isosurface.rst new file mode 100644 index 00000000000..270e349c70a --- /dev/null +++ b/doc/src/fix_graphics_isosurface.rst @@ -0,0 +1,259 @@ +.. index:: fix graphics/isosurface + +fix graphics/isosurface command +=============================== + +Syntax +"""""" + +.. code-block:: LAMMPS + + fix ID group-ID graphics/isosurface Nevery isovalue radius keyword args ... + +* ID, group-ID are documented in :doc:`fix ` command +* graphics/isosurface = style name of this fix command +* Nevery = update graphics information every this many time steps +* isovalue = isovalue for the particle property isosurface selection +* radius = radius describing the spread of the atoms to the density grid (distance units) +* one or more keyword/args pairs may be appended +* keyword = *quality* or *property* or *filename* or *binary* or *pad* + + .. parsed-literal:: + + *quality* keyword = isosurface grid resolution setting + keyword = one of *min*, *low*, *med*, *high*, or *max* + *property* value = per-atom property used to create the isosurface grid + value = *none*, *mass*, c_ID, c_ID[i], f_ID, f_ID[i], v_name + *none* = 1.0 for all atoms + *mass* = mass of the atoms + c_ID = per-atom vector calculated by a compute with ID + c_ID[I] = Ith column of per-atom array calculated by a compute with ID + f_ID = per-atom vector calculated by a fix with ID + f_ID[I] = Ith column of per-atom array calculated by a fix with ID + v_name = per-atom vector calculated by an atom-style variable with name + *filename* name = name pattern for output of a sequence of STL format mesh files (must contain a \* character to be replaced by the timestep number) + *binary* logical = select whether to output a binary STL file (default is text mode) + *pad* number = pad the timestep in the output file name with zeroes to have this many digits (default is 0) + +Examples +"""""""" + +.. code-block:: LAMMPS + + fix sf1 water graphics/isosurface 200 0.1 2.5 quality high property mass + fix stl water graphics/isosurface 200 0.01 1.5 filename water-isosurface-*.stl pad 5 + +Description +""""""""""" + +.. versionadded:: TBD + +This fix allows to add an isosurface graphics object representing the +triangulated isosurface at a given isovalue on a grid to images rendered +with :doc:`dump image ` using the *fix* keyword and +optionally to output the computed mesh as a series of STL format files +for external processing. + +The *group-ID* sets the group ID of the atoms selected to be represented +by the isosurface. This may be a dynamic group. + +The *Nevery* keyword determines how often the isosurface graphics data +is updated. This should be the same value as the corresponding *N* +parameter of the :doc:`dump ` image command. LAMMPS will stop +with an error message if the settings for this fix and the dump command +are not compatible. + +The isosurface objects will be colored by the atom type that is closest +to each isosurface grid cell when the *type* coloring scheme is used in +the :doc:`dump image fix ` command. The color is that of +the atom type's element color instead with the *element* coloring +scheme, or just a globally set constant color for the whole isosurface +with the *const* coloring scheme. That color can be set with the +*fcolor* keyword of the :doc:`dump modify ` command. + +The isosurface's transparency setting is fully opaque by default and can +be changed with the *ftrans* keyword of the :doc:`dump modify +` command. + +The *isovalue* argument sets the isovalue used to compute the +isosurface. The optimum value depends on the property on that is being +used and the information that is supposed to be conveyed. It usually +requires some experimentation in combination with varying the *radius* +setting. + +The *radius* argument sets the width of the gaussian distribution +function used to distribute the per-particle data across the grid. Its +value controls the smoothness of the isosurface and - as mentioned +above - may need some experimentation in combination with the choice of +*isovalue* to achieve the desired output. + +The *quality* keyword can have any of these words as argument: "min", +"low", "med", "high", or "max", and selects the grid resolution used +for the isosurface. The actual grid dimensions depend on the geometry +of the simulation cell. + +The optional *property* keyword controls what property is used to set +the values at the grid points for the isosurface. The default setting +of *none* just uses a value of 1.0, resulting in the data grid +representing a smoothed out number density. Other possible arguments +are *mass* (for representing the smoothed out mass density) or a +references to a a :doc:`compute `, a :doc:`fix `, or a +reference to an atom-style :doc:`variable `. The compute or +fix must produce a per-atom vector or array, not a global or local +quantity. In case the property is a per-atom array, the column must be +selected. + +The optional *filename* keyword controls whether the computed triangle +mesh is exported to an `STL format file +`_ for use with +external visualization programs or 3d-printers. The filename must +contain a star character (\*) which will be replaced by the timestep +number. There is a new file created for every timestep. + +If LAMMPS has been compiled with the :doc:`corresponding setting +` and if the filename ends with ".gz" or some other +:ref:`supported compression format suffix `, the STL file is +written in compressed format. A compressed STL file can be +:math:`5-10\times` smaller than the text version, but may need to be +uncompressed before it can be read into a graphics program. + +The optional *binary* keyword controls whether the STL format output +file is in ASCII text mode (the default when the keyword is not used or +when using "no" or "off" as argument) or in binary mode. Binary STL +files are about :math:`4-5\times` smaller than the ASCII text version, +and can be written and read *much* faster. Not all programs that handle +STL files can read binary files and thus they may be converted to ASCII +format. LAMMPS includes the :ref:`stl_bin2text ` program +for that purpose. + +----------- + +Dump image info +""""""""""""""" + +.. versionadded:: TBD + +Fix graphics/isosurface is designed to be used with the *fix* keyword of +:doc:`dump image `. The fix will construct an isosurface +based on the atom positions, the selected property. of the atoms in the +fix group and pass the graphics geometry information about it to *dump +image* so that it is included in the rendered image. + +The *fflag1* setting of *dump image fix* determines whether the +isosurface will be rendered as a set of connected triangles (1) or as a +mesh of cylinders (2). + +If using a mesh of cylinders, the *fflag2* setting determines the +diameter of the cylinders. + +The *quality* settings of "min" and "low" work best with the cylinder +mesh setting while the other quality settings are more suitable for a +triangle mesh. + +Example for using STL output in VMD +""""""""""""""""""""""""""""""""""" + +Below is an example input commands showcasing the use of the +*graphics/isosurface* fix and exporting STL files. They are added to a +simulation of :doc:`a bulk SPC/E water system ` with 1350 +water molecules. + +.. code-block:: LAMMPS + + region center sphere 10.0 10.0 10.0 10.0 units box + group sphere dynamic all region center + + compute prop all property/atom mass + fix surf sphere graphics/isosurface 10 2.0 2.0 quality high property c_prop filename sphere-*.stl pad 5 + + dump viz sphere image 10 sphere-lammps-*.png type type size 600 600 zoom 1.6 shiny 0.4 fsaa yes & + view 70 -20 box no 0.025 fsaa yes bond atom 0.5 fix surf const 1 0.2 + dump_modify viz pad 5 backcolor2 gray adiam 1 2.432 adiam 2 1.92 & + acolor 1 firebrick acolor 2 silver fcolor surf forestgreen ftrans surf 0.25 + + dump xyz sphere xyz 10 sphere.xyz + dump_modify xyz element O H + +With the following script (use ``vmd -e -eofexit vizsphere.vmd`` to run +the script) the first frame of the trajectory of those selected atoms +and the corresponding STL file are then loaded into `VMD +`_ via VMD/Tcl script commands +and then rendered with both OpenGL (which is what you see on the +screen) and then also with the `Tachyon ray tracing program +`_ included with VMD. The +following images compare the LAMMPS output with the VMD OpenGL output +and the Tachyon ray tracer (from left to right). + +.. code-block:: Tcl + + display projection Orthographic + display depthcue off + display backgroundgradient on + display shadows on + display ambientocclusion on + display aoambient 0.800000 + display aodirect 0.300000 + display resize 600 600 + + mol new sphere.xyz type xyz first 0 last 0 step 1 autobonds 1 waitfor all + mol delrep 0 top + mol representation VDW 0.300000 12.000000 + mol color Name + mol selection {all} + mol material AOShiny + mol addrep top + mol representation DynamicBonds 1.000000 0.200000 12.000000 + mol color Name + mol selection {all} + mol material AOShiny + mol addrep top + graphics top delete all + graphics top color green + graphics top material BlownGlass + mol addfile sphere-00000.stl type stl waitfor all + + render snapshot vmdscene.tga convert %s sphere-opengl.png + render TachyonInternal vmdscene.tga convert %s sphere-raytrace.png + rm vmdscene.tga + + +.. |surface1| image:: img/isosurface-lammps.png + :width: 33% +.. |surface2| image:: img/isosurface-opengl.png + :width: 33% +.. |surface3| image:: img/isosurface-raytrace.png + :width: 33% + +|surface1| |surface2| |surface3| + +.. raw:: html + +
(Fix graphics/isosurface visualization and exporty example. Click to see the full-size images)

+ +Restart, fix_modify, output, run start/stop, minimize info +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +No information about this fix is written to :doc:`binary restart files +`. + +None of the :doc:`fix_modify ` options apply to this fix. + +Restrictions +"""""""""""" + +This fix is part of the GRAPHICS package. It is only enabled if LAMMPS +was built with that package. See the :doc:`Build package +` page for more info. + +Related commands +"""""""""""""""" + +:doc:`fix graphics/arrows `, +:doc:`fix graphics/labels `, +:doc:`fix graphics/objects `, +:doc:`fix graphics/periodic `, + +Defaults +"""""""" + +quality = low, property = none, binary = no, pad = 0, filename = none diff --git a/doc/src/fix_graphics_labels.rst b/doc/src/fix_graphics_labels.rst new file mode 100644 index 00000000000..87cd57ae0c2 --- /dev/null +++ b/doc/src/fix_graphics_labels.rst @@ -0,0 +1,314 @@ +.. index:: fix graphics/labels + +fix graphics/labels command +=========================== + +Syntax +"""""" + +.. code-block:: LAMMPS + + fix ID group-ID graphics/labels Nevery mode keyword args ... + +* ID, group-ID are documented in :doc:`fix ` command +* graphics/labels = style name of this fix command +* Nevery = update graphics information every this many time steps +* zero or more keyword/args groups may be appended +* keyword = *image* or *text* + + .. parsed-literal:: + + *image* filename x y z keyword args = display image in visualization + filename = name of the image file + x, y, z = position where the center of the image is located in the visualization + any of x, y, or z can be a variable (see below) + one or more keyword/arg pairs may be appended + keyword = *scale* or *transcolor* + *scale* value = the image is scaled by this value (default 1.0), can be a variable (see below) + *transcolor* arg = select color for transparency: *auto* or *none* or *r/g/b* + *auto* = uses the color in the lower left corner of the image for transparency + *none* = disables transparency + *r/g/b* = provide three integers in the range 0 to 255 to select transparancy color in RGB color space + + *text* labeltext x y z keyword args = display text in visualization + labeltext = text for the label, must be quoted if it contains whitespace + x, y, z = position where the center of the text is located in the visualization + any of x, y, or z can be a variable (see below) + + keyword = *fontcolor* or *framecolor* or *backcolor* or *transcolor* or *size* + *fontcolor* arg = select color for text: *white* (default) or *black* or *r/g/b* + *white* = uses white + *black* = uses black + *r/g/b* = provide three integers in the range 0 to 255 + *framecolor* arg = select color for frame around text: *silver* (default) or *darkgray* or *white* or *black* or *r/g/b* + *silver* = uses a very light gray + *darkgray* = uses a very dark gray + *white* = uses white + *black* = uses black + *r/g/b* = provide three integers in the range 0 to 255 + *backcolor* arg = select color for background of the text: *silver* (default) or *darkgray* or *white* or *black* *r/g/b* + *silver* = uses a very light gray + *darkgray* = uses a very dark gray + *white* = uses white + *black* = uses black + *r/g/b* = provide three integers in the range 0 to 255 + *transcolor* arg = select color for transparency: *silver* (default) or *darkgray* or *white* or *black* or *none* or *r/g/b* + *silver* = uses a very light gray + *darkgray* = uses a very dark gray + *white* = uses white + *black* = uses black + *none* = disables transparency + *r/g/b* = provide three integers in the range 0 to 255 + *size* value = set the size of the characters (default 24), can be a variable (see below) + +Examples +"""""""" + +.. code-block:: LAMMPS + + fix pix all graphics/labels 100 image teapot.png 5.0 -1.0 -2.0 transcolor auto + fix pot all graphics/labels 100 image teapot.ppm 1.0 v_ypos v_zpos scale v_prog transcolor 19/92/192 + fix lbl all graphics/labels 1000 text "LAMMPS graphics demo" 5.0 -1.0 -2.0 backcolor darkgray framecolor black + fix info all graphics/labels 1000 text "Step: $(step) Angle: ${rot}" 5.0 -1.0 -2.0 size 32 + +Description +""""""""""" + +.. versionadded:: TBD + +This fix allows to add either images or text as "labels" to :doc:`dump +image ` created images by using the *fix* keyword. This can +be useful to augment images with additional graphics or text directly +and without having to post-process the images. Since the positioning +uses the coordinate system of the simulation and because the graphics +objects use the depth buffer of the image rasterizer, atoms and other +graphics in the "scene" can be located before or behind any text or +image label. + +The *group-id* is ignored by this fix. + +The *Nevery* keyword determines how often the graphics data is updated. +This should be the same value as the corresponding *N* parameter of the +:doc:`dump ` image command. LAMMPS will stop with an error +message if the settings for this fix and the dump command are not +compatible. + +The *image* keyword reads an image file and adds it to the visualization +centered around the provided position and optionally scaled by the +provided scale factor. The filename suffix determines whether LAMMPS +will try to read a file in JPEG, PNG, or PPM format. If the suffix is +".jpg" or ".jpeg", then LAMMPS attempts to read the image in `JPEG +format `_, if the suffix is ".png", then lammps attempts +to read the image in `PNG format `_. Otherwise LAMMPS will +try to read the image in `ppm (aka netpbm) format `_. Not +all variants of those file formats are compatible with image reader code +in LAMMPS. If LAMMPS encounters an incompatible or unrecognizable file +format or a corrupted file, it will stop with an error. + +If LAMMPS detects during a run that the file has been changed, it will +re-read it. This allows for instance to create a plot using internal +LAMMPS data or from processing an output file during the simulation with +the `matplotlib `_ python module using a +:doc:`python ` command and :doc:`fix python/invoke +` and then embed the resulting image into the +dump image output. See below for a minimal example for such a setup. + +When using the *image* keyword, the name of the image file and its position +in the "scene" are required arguments. Optional keyword / value pairs +may be added: + + The *scale* value determines if the image is scaled before it is added + to the :doc:`dump image ` output. LAMMPS currently + employs a bilinear scaling algorithm. + + The *transcolor* value selects a color for transparency. All pixels + in the image with that color will be skipped when the image is + rendered. The color is specified as an R/G/B triple with values + ranging from 0 to 255 for each channel. There are also two special + arguments: *auto* will pick the color of the pixel in the lower left + corner as transparency color and *none* will disable all transparency + processing (this is the default). + + +The *text* keyword will process a provided text into a pixmap and adds +it to the visualization centered around the provided position in a +similar fashion as with the *image* keyword. The requirements for the +text argument are the same as in the :doc:`fix print ` +command: it must be a single argument, so text with whitespace must be +quoted; and the text may contain equal style or immediate variables +using the ``${name}`` or ``$(expression)`` format. The variables are +evaluated and expanded at every *Nevery* time step. + +When using the *text* keyword, the text and its position in the "scene" +are required arguments. Optional keyword / value pairs may be added: + + The *size* value determines the size of the letters in the text in + pixels (approximately) and values between 4 and 512 are accepted. + The default value is 24. + + There are four color settings: *fontcolor* or *framecolor* or + *backcolor* or *transcolor*\ . The color can be specified for all of + those either as an R/G/B triple with values ranging from 0 to 255 for + each channel (e.g. yellow would be "255/255/0"). There are also a few + shortcuts for common choices: *silver*, *darkgray*, *white*, *black*. + The default *backcolor* value is *silver*. + + - *fontcolor* selects the color for the text, default is *white* + - *backcolor* selects the color for the background, default is + *silver* + - *framecolor* selects the color for the frame around the background, + default is *silver*. + - *transcolor* value selects a color for transparency, default is + *silver*. If this color is the same as any of the other color + settings, those pixels are not drawn. Thus with the default + settings, the text will be rendered in white without background or + frame. The *none* setting for *transcolor* disables transparency + processing. + + When rendering text with transparent background it is recommended to + select a similar color but slightly darker or brighter color as background. + This will reduce unwanted color effects at the edges due to anti-aliasing. + +There may be multiple *image* or *text* keywords with their arguments +in a single fix *graphics/labels* command. + +The arguments for the positions of an *image* or *text* and the *scale* +factor of an *image* or the *size* of a *text* can be specified as +equal-style :doc:`variables `, namely *x*, *y*, *z*, *scale*, +or *size*. If any of these values is a variable, it should be specified +as `v_name`, where `name` is the variable name. In this case, the +variable will be evaluated each *nevery* timestep, and its value used to +position and resize the image or text. Please see the documentation of +the :doc:`fix graphics/objects ` command for a +more detailed discussion on using variables with graphics objects. + +.. _jpeg_format: https://jpeg.org/jpeg/ +.. _png_format: https://en.wikipedia.org/wiki/portable_network_graphics +.. _ppm_format: https://en.wikipedia.org/wiki/netpbm + +----------- + +Dump image info +""""""""""""""" + +The fix graphics/labels command is designed to be used with the *fix* +keyword of :doc:`dump image `. The fix adds images or text +to the visualization. + +The color style setting for the fix in the :doc:`dump image +` has no effect on either image or text labels. The +transparency is by default fully opaque and can be changed with +*dump\_modify ftrans*\ . + +The *fflag1* and *fflag2* settings of *dump image fix* are ignored. + +-------------- + +Including a "dynamic" image +""""""""""""""""""""""""""" + +The LAMMPS input commands below provide a demonstration for creating and +updating a plot during a run and importing it into a visualization. +This requires to compile LAMMPS with the :ref:`PYTHON ` +package and also compile and install the :doc:`LAMMPS Python module +`. + +The first :doc:`python ` command loads the `matplotlib `_ +and LAMMPS Python modules and configures *matplotlib* to use the `non-interactive 'agg' +backend `_ +for creating image files. + +The second :doc:`python ` command defines the ``myplot()`` +Python function that is supposed to be called regularly during the run +from :doc:`fix python/invoke `. This function has to +accept two arguments, the LAMMPS object pointer and an integer as +required by the fix. The LAMMPS object pointer can be utilized to query +the running LAMMPS instance about internal data. In this example, we +only retrieve the LAMMPS version and add it to the plot title. By +default, plots have an opaque white background and black lines and text. +In order to overlay the plot as a transparent image, all lines and text +are set to use the color white, while backgrounds are set to use a very +bright gray (to minimize anti-aliasing artifacts when deleting the +background pixels). + +The two fix commands invoke the python function and read and make the +resulting PNG format image available to dump image. The plot is updated +only for every 10th dumped image. + +The final lines are :doc:`dump image ` commands for integrating +the generated plot into the visualization of the atom. + +.. code-block:: LAMMPS + + python source here """ + import matplotlib + matplotlib.use('agg') + import matplotlib.pyplot as plt + from lammps import lammps + """ + + python myplot input 2 SELF 0 format pi here """ + def myplot(lmpptr, vflag): + lmp = lammps(ptr=lmpptr) + fig, ax = plt.subplots(facecolor=(0.9,0.9,0.9),edgecolor='white') + ax.set_facecolor((0.9,0.9,0.9)) + ax.set_title('Demo Plot: LAMMPS version ' + str(lmp.version()) ,color='white') + ax.set_xlabel('Time',color='white') + ax.set_ylabel('Value',color='white') + ax.tick_params(colors='white') + for spine in ax.spines.values(): + spine.set_edgecolor('white') + ax.plot([1,2,3,4,5,6],[2,3,2.5,3.1,2.8,3.0],color='white', linestyle='--') + fname = 'myplot.png' + plt.savefig(fname,dpi=180) + plt.close() + """ + + fix plot all python/invoke 1000 post_force myplot + fix label all graphics/labels 100 image myplot.png 20.0 9.0 10.0 transcolor auto scale 0.5 + + dump viz all image 100 image-*.png type type size 800 800 view 80 10 box yes 0.02 & + fsaa yes shiny 0.1 ssao yes 23154 0.8 zoom 1.4 fix label type 0 0 + dump_modify viz pad 6 backcolor2 gray backcolor darkgray boxcolor silver + +.. figure:: JPG/fix_graphics_labels_plot.png + :align: center + :scale: 50% + + Example image output for adding the above commands to the ``melt`` example. + +--------- + +Restart, fix_modify, output, run start/stop, minimize info +========================================================== + +No information about this fix is written to :doc:`binary restart files +`. + +None of the :doc:`fix_modify ` options apply to this fix. + +Restrictions +"""""""""""" + +This fix is part of the GRAPHICS package. It is only enabled if LAMMPS +was built with that package. See the :doc:`Build package +` page for more info. + +To read JPEG or PNG format images, support for the corresponding +graphics libraries must have been compiled and linked into LAMMPS. +Please see the :ref:`instructions for building LAMMPS with the GRAPHICS +package ` for more information on how to do that. + +Related commands +"""""""""""""""" + +:doc:`fix print `, +:doc:`fix graphics/arrows `, +:doc:`fix graphics/isosurface `, +:doc:`fix graphics/objects `, +:doc:`fix graphics/periodic ` + +Default +""""""" + +transcolor = "none" for *image* and "silver" for *text*, scale = 1.0, fontcolor = white, backcolor = silver, framecolor = silver, size = 24 diff --git a/doc/src/fix_graphics.rst b/doc/src/fix_graphics_objects.rst similarity index 59% rename from doc/src/fix_graphics.rst rename to doc/src/fix_graphics_objects.rst index b3247e2f976..e077f1aed46 100644 --- a/doc/src/fix_graphics.rst +++ b/doc/src/fix_graphics_objects.rst @@ -1,20 +1,20 @@ -.. index:: fix graphics +.. index:: fix graphics/objects -fix graphics command -==================== +fix graphics/objects command +============================ Syntax """""" .. code-block:: LAMMPS - fix ID group-ID graphics Nevery keyword args ... + fix ID group-ID graphics/objects Nevery keyword args ... * ID, group-ID are documented in :doc:`fix ` command -* graphics = style name of this fix command +* graphics/objects = style name of this fix command * Nevery = update graphics information every this many time steps * one or more keyword/args pairs may be appended -* keyword = *sphere* or *cylinder* or *arrow* or *progbar* +* keyword = *sphere* or *cylinder* or *arrow* or *cone* or *progbar* .. parsed-literal:: @@ -24,16 +24,23 @@ Syntax R = sphere radius (distance units) any of x, y, z, and R can be a variable (see below) *cylinder* args = type x1 y1 z1 x2 y2 z2 R - type = an atom type value to select the color of the sphere + type = an atom type value to select the color of the cylinder x1, y1, z1, x2, y2, z2 = positions of the centers at the two ends of the cylinder (distance units) R = cylinder radius (distance units) any of x1, y1, z1, x2, y2, z2, and R can be a variable (see below) *arrow* args = type x1 y1 z1 x2 y2 z2 R ratio - type = an atom type value to select the color of the sphere - x1, y1, z1, x2, y2, z2 = positions of the centers at the tip and the bottom of the arrow (distance units) + type = an atom type value to select the color of the arrow + x1, y1, z1, x2, y2, z2 = positions of the centers at the bottom (x1,y1,z1) and the tip (x2,y2,z2) of the arrow (distance units) R = cylinder radius (distance units) ratio = tip to body ratio (unitless) any of x1, y1, z1, x2, y2, z2, and R can be a variable (see below) + *cone* args = type x1 y1 z1 x2 y2 z2 R1 R2 sides + type = an atom type value to select the color of the cone + x1, y1, z1, x2, y2, z2 = positions of the centers at the bottom (x1,y1,z1) and the top (x2,y2,z2) of the arrow (distance units) + R1 = bottom radius (distance units) + R2 = top radius (distance units) + sides = bitmap value between 0 and 7 deciding whether bottom cap (1), top cap (2) or side (4) is drawn (unitless) + any of x1, y1, z1, x2, y2, z2, R1 and R2 can be a variable (see below) *progbar* args = type1 type2 dim x y z length R ratio tics type1 = an atom type value to select the color of the progress bar body and the tics type2 = an atom type value to select the color of the progress indicator @@ -50,9 +57,9 @@ Examples .. code-block:: LAMMPS - fix 1 all graphics 100 sphere 1 0.0 0.0 15.0 3.0 sphere 2 0.0 0.0 5.0 1.0 - fix 1 all graphics 1000 sphere 1 v_x v_y 0.0 v_radius cylinder 1 v_x v_y 0.0 v_x v_y 10.0 3.0 - fix 2 all graphics 100 progbar 3 1 z 0.012 -0.012 0.0025 0.03 0.0003 v_prog 10 + fix 1 all graphics/objects 100 sphere 1 0.0 0.0 15.0 3.0 sphere 2 0.0 0.0 5.0 1.0 + fix 1 all graphics/objects 1000 sphere 1 v_x v_y 0.0 v_radius cylinder 1 v_x v_y 0.0 v_x v_y 10.0 3.0 + fix 2 all graphics/objects 100 progbar 3 1 z 0.012 -0.012 0.0025 0.03 0.0003 v_prog 10 Description """"""""""" @@ -62,6 +69,8 @@ Description This fix allows to add arbitrary objects to images rendered with :doc:`dump image ` using the *fix* keyword. +The *group-ID* is ignored by this fix. + The *Nevery* keyword determines how often the graphics object data is updated. This should be the same value as the corresponding *N* parameter of the :doc:`dump ` image command. LAMMPS will stop @@ -73,49 +82,51 @@ Available graphics objects are (see above for exact command line syntax): - *sphere* - a sphere defined by its center location and its radius - *cylinder* - a cylinder defined by its two center endpoints and its radius - *arrow* - a cylinder with a cone at one side (see note below) +- *cone* - a truncated cone with a flat circular cap at either side (see note below) - *progbar* - progress bar a long a selected axis and with optional tick marks The *type* quantity determines the color of the object. Its represents an *atom* type and the object will be colored the same as the -corresponding atom type when the *type* coloring scheme is used in the -:doc:`dump image fix ` command is used. The color may also -be that of the atom type's element or just a globally set constant color -for *all* objects of this fix instance, which can be changed using a -:doc:`dump modify fcolor ` command. For the *progbar* -object *two* atom type values must be specified. +corresponding atom type when the "type" or "element" color style is used +in the :doc:`dump image fix ` command. For the *progbar* +object **two** atom type values must be specified. For color style +"const" the color will be set globally to the same color for *all* +objects of this fix instance, which can be changed using a :doc:`dump +modify fcolor ` command. The transparency is by default +fully opaque and can be changed globally with *dump\_modify ftrans*\ . The *x*\, *y*\, and *z* parameters correspond to the position of the center of the object (*sphere* and *progbar*). *x1*\, *y1*\, and *z1* as well as *x2*\, *y2*\, and *z2* are instead representing the top and -bottom position of a graphics object (*cylinder* and *arrow*). The *R* -parameter determines the radius. +bottom position of a graphics object (*cylinder*, *arrow*, and *cone*). +The *R* parameter determines the radius. For the *cone* object there is +a bottom radius (*R1*) and top radius (*R2*). + +The *cone* object has an additional setting that selects whether the +circular cap at the bottom (value = 1), or the circular cap at the top +(value = 2) or the side (value = 4) is drawn. The values are added and +thus if the cone with both caps and the side should be drawn the +required sides setting would be 7. The *progbar* object has four additional parameters: *dim* sets the direction of the progress bar, "x", "y", or "z"; *length* sets the -length of the entire object; *ratio* sets the ratio of progress and -is expected to be between 0.0 and 1.0 (larger or smaller values will -be reset to 1.0 or 0.0, respectively); and *tics* determines the number -of tics shown on the progress bar, this must be a number between 0 and 20. +length of the entire object; *ratio* sets the ratio of progress and is +expected to be between 0.0 and 1.0 (larger or smaller values will be +reset to 1.0 or 0.0, respectively); and *tics* determines the number of +tics shown on the progress bar, this must be a number between 0 and 20. Unlike for the other graphics objects, all settings except for *ratio* are fixed and cannot be a variable reference. -.. admonition:: Work in progress notice - :class: note - - The *arrow* object is currently composed of two cylinders since the - :doc:`dump image ` render implementation is missing a - primitive to render a cone. This fix will be updated when that - functionality becomes available. - ---------------------- Many of the quantities defining a graphics object can be specified as an equal-style :doc:`variable `, namely *x*, *y*, *z*, or *R* for -a *sphere* or namely *x1*, *y1*, *z1*, *x2*, *y2*, *z2*, or *R* for a -*cylinder*. If any of these values is a variable, it should be -specified as `v_name`, where `name` is the variable name. In this case, -the variable will be evaluated each *Nevery* timestep, and its value -used to define the indenter geometry. +a *sphere* or *x1*, *y1*, *z1*, *x2*, *y2*, *z2*, or *R* for a +*cylinder* or *x1*, *y1*, *z1*, *x2*, *y2*, *z2*, *R1*, or *R2* for a +*cone*. If any of these values is a variable, it should be specified as +`v_name`, where `name` is the variable name. In this case, the variable +will be evaluated each *Nevery* timestep, and its value used to define +the graphics object location, orientation, or size. Note that equal-style variables can specify formulas with various mathematical functions, and include :doc:`thermo_style ` @@ -158,26 +169,27 @@ Dump image info .. versionadded:: TBD -Fix graphics is designed to be used with the *fix* keyword of :doc:`dump +Fix graphics/objects is designed to be used with the *fix* keyword of :doc:`dump image `. The fix will pass geometry information about the objects listed on the command line to *dump image* so that they are included in the rendered image. The *fflag1* setting of *dump image fix* determines whether cylinder -elements are capped with spheres: 0 means no caps, 1 means the lower -end is capped, 2 means the upper end is capped, and 3 means both ends -are capped. This applies to the *cylinder* object and also to the -body of the *arrow* object and the elements of the *progbar* object. +elements are capped with spheres: 0 means no caps, 1 means the lower end +is capped, 2 means the upper end is capped, and 3 means both ends are +capped. This applies to the *cylinder* object and the elements of the +*progbar* object. The *fflag2* setting allows you to adjust the radius of the rendered -sphere and cylinder items comprising the objects. Since the radius of -these objects is an input parameter for this fix, it is recommended to -set this flag to 0.0. +sphere, cylinder or cone items comprising the objects. Since the radius +of these objects is an input parameter for this fix, it is recommended +to set this flag to 0.0. .. figure:: JPG/fix-graphics-example.png :figclass: align-center - Example of graphics objects rendered with *fix graphics* with *fflag1* setting of 0 (left) and 3 (right) + Example of graphics objects rendered with *fix graphics/objects* with + *fflag1* setting of 0 (left) and 3 (right) These images were created with the following input file: @@ -185,27 +197,26 @@ These images were created with the following input file: units si region simulation_box block -0.01 0.01 -0.01 0.01 -0.01 0.01 units box - create_box 4 simulation_box + create_box 5 simulation_box mass * 1 variable xpos equal 0.004*sin(PI*step/1000) variable ypos equal 0.004*cos(PI*step/1000) variable zpos equal 5.0*v_xpos variable prog equal (step)/10000.0 - fix gra all graphics 50 sphere 1 v_xpos v_ypos -0.009 0.002 & + fix gra all graphics/objects 50 sphere 5 v_xpos v_ypos -0.009 0.002 & sphere 1 0.01 -0.005 0.01 0.005 & progbar 3 1 z 0.012 -0.012 0.002 0.02 0.0005 v_prog 10 & - cylinder 4 0.01 -0.005 -0.01 0.01 -0.005 0.01 0.005 & - arrow 2 v_xpos v_ypos 0.0 v_xpos v_ypos 0.01 0.0005 0.2 + cylinder 4 0.01 -0.005 -0.01 0.01 -0.005 0.01 0.003 & + arrow 2 v_xpos v_ypos 0.0 v_xpos v_ypos 0.01 0.002 0.4 - dump viz all image 100 myimage2-*.ppm type type size 600 600 zoom 1.24872 & + dump viz all image 100 myimage2-*.ppm type type size 500 600 zoom 1.24872 & shiny 0.2 fsaa yes ssao yes 4539 0.6 box no 0.01 axes no 0.5 0.025 & - fix gra type 0 0 view 75 5 + fix gra type 3 0 view 80 10 center s 0.4 0.3 0.4 dump_modify viz pad 9 backcolor black acolor 3 gray run 2000 - Restart, fix_modify, output, run start/stop, minimize info """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -217,12 +228,17 @@ None of the :doc:`fix_modify ` options apply to this fix. Restrictions """""""""""" -none +This fix is part of the GRAPHICS package. It is only enabled if LAMMPS +was built with that package. See the :doc:`Build package +` page for more info. Related commands """""""""""""""" -none +:doc:`fix graphics/arrows `, +:doc:`fix graphics/labels `, +:doc:`fix graphics/isosurface `, +:doc:`fix graphics/periodic ` Default """"""" diff --git a/doc/src/fix_graphics_periodic.rst b/doc/src/fix_graphics_periodic.rst new file mode 100644 index 00000000000..662eb0a6395 --- /dev/null +++ b/doc/src/fix_graphics_periodic.rst @@ -0,0 +1,126 @@ +.. index:: fix graphics/periodic + +fix graphics/periodic command +============================= + +Syntax +"""""" + +.. code-block:: LAMMPS + + fix ID group-ID graphics/periodic Nevery keyword args ... + +* ID, group-ID are documented in :doc:`fix ` command +* graphics/periodic = style name of this fix command +* Nevery = update graphics information every this many time steps +* zero or more keywords or keyword/value pairs may be appended +* keyword = *xlo* or *xhi* or *ylo* or *yhi* or *zlo* or *zhi* or *radius* or *atoms* or *bonds* + + .. parsed-literal:: + + *xlo*, *xhi*, *ylo*, *yhi*, *zlo*, *zhi* = enable periodic images of atoms and bonds to either side of the simulation box in the given direction + *radius* value = sets the atom radius + value = either "auto" or a number (distance units) + *atoms* yes/no = enables or disables displaying periodic images of atoms + *bonds* yes/no = enables or disables displaying periodic images of bonds + +Examples +"""""""" + +.. code-block:: LAMMPS + + fix vec all graphics/periodic 10 ylo zhi zlo yhi xlo + fix vec all graphics/periodic 1000 ylo zhi zlo yhi bonds no radius 0.5 + +Description +""""""""""" + +.. versionadded:: TBD + +This fix allows to add graphics of periodic images of atoms and bonds to +:doc:`dump image ` images using the *fix* keyword. This can +be useful to visualize periodic systems. + +The *group-ID* sets the group ID of the atoms selected to be displayed +as periodic images. For bonds to be displayed, *both* atoms of the bond +have to be inside the group. + +The *Nevery* keyword determines how often the arrows graphics data is +updated. This should be the same value as the corresponding *N* +parameter of the :doc:`dump ` image command. LAMMPS will stop +with an error message if the settings for this fix and the dump command +are not compatible. + +The *xlo*, *xhi*, *ylo*, *yhi*, *zlo*, *zhi* keywords, if set, enable +display of a periodic image of the system to the corresponding side in +the corresponding direction of the principal simulations cell. If all +keywords are used, there will be 26 additional copies of the system +rendered. + +The *radius* keyword determines the radius of the atoms. If a value of +"auto" is used, the radius is inherited from the atom type. + +----------- + +Dump image info +""""""""""""""" + +.. versionadded:: TBD + +Fix graphics/periodic is designed to be used with the *fix* keyword of +:doc:`dump image `. The fix adds graphics objects of +periodic images of atoms and bonds in the fix group to *dump image* so +that they are included in the rendered image. + +The color of the atoms and bonds is by default the same as that of the +atoms and bonds in the principal simulation cell when using color styles +"type" or "element" with the fix command. With fix color style "const" +the default value of "white" can be changed using :doc:`dump_modify +fcolor `. The transparency is by default fully opaque and +can be changed with *dump\_modify ftrans*\ . + +The *fflag1* setting of dump of *dump image fix* determines if the bonds +are capped with spheres: a value of 0 means no caps, a value of 1 a cap +at the lower end, a value of 2 a cap at the upper end, and a value of 3 +caps at both ends. When also replicating atoms, a value other than 0 +would be redundant, otherwise a value of 3 is probably the desired +choice. + +The *fflag2* settings of *dump image fix* allows to modify the bond +diameter relative to the automatically chosen one. In most use cases a +value of 0.0 is probably the desired choice. + +Restart, fix_modify, output, run start/stop, minimize info +========================================================== + +No information about this fix is written to :doc:`binary restart files +`. + +None of the :doc:`fix_modify ` options apply to this fix. + +Restrictions +"""""""""""" + +This fix is part of the GRAPHICS package. It is only enabled if LAMMPS +was built with that package. See the :doc:`Build package +` page for more info. + +Currently only periodic images of atoms and bonds in each direction can +be displayed. + +Body particles or ellipsoids and similar are not fully supported; they are +shown as spheres with this fix. + +Related commands +"""""""""""""""" + +:doc:`fix graphics/arrows `, +:doc:`fix graphics/labels `, +:doc:`fix graphics/isosurface `, +:doc:`fix graphics/objects ` + +Default +""""""" + +radius = auto, atoms = yes, bonds = yes, if supported by atom style otherwise no, +no periodic graphics diff --git a/doc/src/fix_indent.rst b/doc/src/fix_indent.rst index 56d9afc28a0..9a5cd6285b9 100644 --- a/doc/src/fix_indent.rst +++ b/doc/src/fix_indent.rst @@ -209,10 +209,15 @@ Dump image info Fix indent supports the *fix* keyword of :doc:`dump image `. The fix will pass geometry information about the indenter to *dump image* so that the indenter object will be included in the rendered -image. This feature currently only supports spherical, cylindrical, and -planar indenters. Please note, that for :doc:`2d systems `, -a planar indenter rendered as a plane would be invisible and it is thus -rendered as a cylinder. +image. Please note, that for :doc:`2d systems `, a planar +indenter rendered as a plane would be invisible and it is thus rendered +as a cylinder. + +The color of the indenter object is by default that of the first atom +type when using color styles "type" or "element". With color style +"const" the default value of "white" can be changed using +:doc:`dump_modify fcolor `. The transparency is by default +fully opaque and can be changed with *dump\_modify ftrans*\ . The *fflag1* setting of *dump image fix* has no impact on rendering a spherical indenter or a planar indenter in 3d systems. For a @@ -223,15 +228,12 @@ and 3 means both ends are capped. The *fflag2* setting allows you to adjust the radius of the rendered object for spherical indenters, cylindrical indenters, and planar -indenters in 2d systems. In many cases you want to use a value < 0 to -reduce the radius of the rendered object so that it does not obscure +indenters in 2d systems. In many cases you want to use a negative value +to reduce the radius of the rendered object so that it does not obscure atoms close to it. For a planar indenter in 2d systems, it should be -set to a value > 0 or the indenter will not be visible since the -diameter is set internally to zero in that case due to lack of a -suitable heuristic for deriving a meaningful diameter. For a planar -indenter in a 3d system, the *fflag2* value sets the transparency of the -plane. It should be set to a value between 0.0 (invisible) and 1.0 -(fully opaque). +set to a positive value or the indenter will not be visible since there +is no radius parameter associated with it (unlike for spherical or +cylindrical indenters) and thus its radius is set to zero internally. Restart, fix_modify, output, run start/stop, minimize info """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" diff --git a/doc/src/fix_reaxff_bonds.rst b/doc/src/fix_reaxff_bonds.rst index 3cff97ae540..6835d66e1fb 100644 --- a/doc/src/fix_reaxff_bonds.rst +++ b/doc/src/fix_reaxff_bonds.rst @@ -35,7 +35,8 @@ is written to *filename* on timesteps that are multiples of *Nevery*, including timestep 0. For time-averaged chemical species analysis, please see the :doc:`fix reaxff/species ` command. -The specified group-ID is ignored by this fix. +The specified group-ID is ignored by this fix except for the :doc:`dump +image ` related functionality (see below). The format of the output file should be reasonably self-explanatory. The meaning of the column header abbreviations is as follows: @@ -82,7 +83,15 @@ Dump image info Fix *reaxff/bonds* supports the *fix* keyword of :doc:`dump image `. The fix will pass geometry information about the bonds computed by the :doc:`ReaxFF pair style ` to *dump image* -so that they can be included in the rendered image. +so that they can be included in the rendered image. Only bonds where +*both* atoms are within the fix group generate graphics objects that are +displayed in the dumped images. That group may be a dynamic group. + +The color of the bonds is by default that of the atoms when using color +styles "type" or "element". With color style "const" the default value +of "white" can be changed using :doc:`dump_modify fcolor `. +The transparency is by default fully opaque and can be changed with +*dump\_modify ftrans*\ . The *fflag1* setting of *dump image fix* determines whether the bonds will be capped with spheres (1) or not (0). diff --git a/doc/src/fix_smd_wall_surface.rst b/doc/src/fix_smd_wall_surface.rst index 53556b940bb..a8eac64cb56 100644 --- a/doc/src/fix_smd_wall_surface.rst +++ b/doc/src/fix_smd_wall_surface.rst @@ -60,13 +60,18 @@ Fix *smd/wall\_surface* supports the *fix* keyword of :doc:`dump image particles to *dump image* so that they be included in the rendered image. +The color of the wall mesh object is by default that of the first atom +type when using color styles "type" or "element". With color style +"const" the default value of "white" can be changed using +:doc:`dump_modify fcolor `. The transparency is by default +fully opaque and can be changed with *dump\_modify ftrans*\ . + The *fflag1* setting of *dump image fix* determines whether the wall will be rendered as a set of connected triangles (1) or as a mesh of cylinders (2). -In case of using triangles, the *fflag2* setting determines the -transparency of the triangles and must use a value between 0.0 -(invisible) and 1.0 (fully opaque). If using a mesh of cylinders, the -*fflag2* setting determines the diameter of the cylinders. +When rendering triangles, the *fflag2* setting is ignored. When using a +mesh of cylinders, the *fflag2* setting determines the diameter of the +cylinders. Restart, fix_modify, output, run start/stop, minimize info """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" diff --git a/doc/src/fix_wall.rst b/doc/src/fix_wall.rst index 1944f8e5565..9c681da66a6 100644 --- a/doc/src/fix_wall.rst +++ b/doc/src/fix_wall.rst @@ -511,6 +511,12 @@ image. Please note, that for :doc:`2d systems `, a wall rendered as a plane would be invisible and it is thus rendered as a cylinder. +The color of the wall is by default that of the first atom type when +using color styles "type" or "element". With color style "const" the +default value of "white" can be changed using :doc:`dump_modify fcolor +`. The transparency is by default fully opaque and can be +changed with *dump\_modify ftrans*\ . + For 2d systems, the *fflag1* setting determines whether the cylinder representing the wall is capped with a sphere at the ends: 0 means no caps, 1 means the lower end is capped, 2 means the upper end is capped, and 3 @@ -520,9 +526,7 @@ cylinder will not be visible since the diameter is set internally to zero due to lack of a suitable heuristic for deriving a meaningful diameter for all types of walls and unit settings. -For 3d systems, the *fflag1* setting is ignored, but the *fflag2* -setting determines the transparency of the wall. It must be set to a -value between 0.0 (invisible) and 1.0 (fully opaque). +For 3d systems, both *fflag1* and *fflag2* are ignored. ----------------- diff --git a/doc/src/fix_wall_body_polygon.rst b/doc/src/fix_wall_body_polygon.rst index 51024767664..9d145a5d77b 100644 --- a/doc/src/fix_wall_body_polygon.rst +++ b/doc/src/fix_wall_body_polygon.rst @@ -15,15 +15,13 @@ Syntax * k_n = normal repulsion strength (force/distance or pressure units) * c_n = normal damping coefficient (force/distance or pressure units) * c_t = tangential damping coefficient (force/distance or pressure units) -* wallstyle = *xplane* or *yplane* or *zcylinder* +* wallstyle = *xplane* or *yplane* * args = list of arguments for a particular style .. parsed-literal:: *xplane* or *yplane* args = lo hi lo,hi = position of lower and upper plane (distance units), either can be NULL) - *zcylinder* args = radius - radius = cylinder radius (distance units) * zero or more keyword/value pairs may be appended to args * keyword = *wiggle* @@ -57,23 +55,20 @@ body particles. The parameters *k_n*, *c_n*, *c_t* have the same meaning and units as those specified with the :doc:`pair_style body/rounded/polygon ` command. -The *wallstyle* can be planar or cylindrical. The 2 planar options -specify a pair of walls in a dimension. Wall positions are given by -*lo* and *hi*\ . Either of the values can be specified as NULL if a -single wall is desired. For a *zcylinder* wallstyle, the cylinder's -axis is at x = y = 0.0, and the radius of the cylinder is specified. - -Optionally, the wall can be moving, if the *wiggle* keyword is -appended. +The *wallstyle* is planar and allows to specify a pair of walls in x- +and y direction each. Wall positions are given by *lo* and *hi*\ . +Either of the values can be specified as NULL if a single wall per +dimension is desired. Optionally, the wall can be moving, if the +*wiggle* keyword is appended. For the *wiggle* keyword, the wall oscillates sinusoidally, similar to -the oscillations of particles which can be specified by the :doc:`fix move ` command. This is useful in packing simulations of +the oscillations of particles which can be specified by the :doc:`fix +move ` command. This is useful in packing simulations of particles. The arguments to the *wiggle* keyword specify a dimension -for the motion, as well as its *amplitude* and *period*\ . Note that -if the dimension is in the plane of the wall, this is effectively a +for the motion, as well as its *amplitude* and *period*\ . Note that if +the dimension is in the plane of the wall, this is effectively a shearing motion. If the dimension is perpendicular to the wall, it is -more of a shaking motion. A *zcylinder* wall can only be wiggled in -the z dimension. +more of a shaking motion. Each timestep, the position of a wiggled wall in the appropriate *dim* is set according to this equation: @@ -87,27 +82,57 @@ the *amplitude*, *omega* is 2 PI / *period*, and *delta* is the time elapsed since the fix was specified. The velocity of the wall is set to the derivative of this expression. +----------------- + +Dump image info +""""""""""""""" + +.. versionadded:: TBD + +This fix supports the *fix* keyword of :doc:`dump image `. +The fix will pass geometry information about the walls to *dump image* +so that the walls will be included in the rendered image. Please note, +that for :doc:`2d systems `, a wall rendered as a plane would +be invisible and it is thus rendered as a cylinder. + +The color of the wall is by default that of the first atom type when +using color styles "type" or "element". With color style "const" the +default value of "white" can be changed using :doc:`dump_modify fcolor +`. The transparency is by default fully opaque and can be +changed with *dump\_modify ftrans*\ . + +The *fflag1* setting determines whether the cylinder representing the +wall is capped with a sphere at the ends: 0 means no caps, 1 means the +lower end is capped, 2 means the upper end is capped, and 3 means both +ends are capped. The *fflag2* setting allows to set the radius of the +rendered cylinders. + +------------------- + Restart, fix_modify, output, run start/stop, minimize info """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" None of the :doc:`fix_modify ` options are relevant to this -fix. No global or per-atom quantities are stored by this fix for -access by various :doc:`output commands `. No parameter -of this fix can be used with the *start/stop* keywords of the -:doc:`run ` command. This fix is not invoked during :doc:`energy minimization `. +fix. No global or per-atom quantities are stored by this fix for access +by various :doc:`output commands `. No parameter of this +fix can be used with the *start/stop* keywords of the :doc:`run ` +command. This fix is not invoked during :doc:`energy minimization +`. Restrictions """""""""""" -This fix is part of the BODY package. It is only enabled if LAMMPS -was built with that package. See the :doc:`Build package ` page for more info. +This fix is part of the BODY package. It is only enabled if LAMMPS was +built with that package. See the :doc:`Build package ` +page for more info. Any dimension (xy) that has a wall must be non-periodic. Related commands """""""""""""""" -:doc:`atom_style body `, :doc:`pair_style body/rounded/polygon ` +:doc:`atom_style body `, +:doc:`pair_style body/rounded/polygon ` Default """"""" diff --git a/doc/src/fix_wall_body_polyhedron.rst b/doc/src/fix_wall_body_polyhedron.rst index ab1c40de851..0f7bee4ec00 100644 --- a/doc/src/fix_wall_body_polyhedron.rst +++ b/doc/src/fix_wall_body_polyhedron.rst @@ -83,6 +83,36 @@ the *amplitude*, *omega* is 2 PI / *period*, and *delta* is the time elapsed since the fix was specified. The velocity of the wall is set to the derivative of this expression. +----------------- + +Dump image info +""""""""""""""" + +.. versionadded:: TBD + +This fix supports the *fix* keyword of :doc:`dump image `. +The fix will pass geometry information about *xplane*\, *yplane*\, and +*zplane* style walls to *dump image* so that the walls will be included +in the rendered image. Please note, that for :doc:`2d systems +`, a wall rendered as a plane would be invisible and it is +thus rendered as a cylinder. + +The color of the wall is by default that of the first atom type when +using color styles "type" or "element". With color style "const" the +default value of "white" can be changed using :doc:`dump_modify fcolor +`. The transparency is by default fully opaque and can be +changed globally with *dump\_modify ftrans*\ . + +For 2d systems, the *fflag1* setting determines whether the cylinder +representing the wall is capped with a sphere at the ends: 0 means no caps, 1 +means the lower end is capped, 2 means the upper end is capped, and 3 +means both ends are capped. The *fflag2* setting allows to set the +radius of the rendered cylinders. + +For 3d systems, both *fflag1* and *fflag2* are ignored. + +------------ + Restart, fix_modify, output, run start/stop, minimize info """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" diff --git a/doc/src/fix_wall_ees.rst b/doc/src/fix_wall_ees.rst index e154d57dea3..f08c90946d2 100644 --- a/doc/src/fix_wall_ees.rst +++ b/doc/src/fix_wall_ees.rst @@ -157,6 +157,41 @@ minimization, invoked by the :doc:`minimize ` command. minimized), you MUST enable the :doc:`fix_modify ` *energy* option for this fix. +----------------- + +Dump image info +""""""""""""""" + +.. versionadded:: TBD + +Fix *wall/ees* fix supports the *fix* keyword of :doc:`dump image +`. The fix will pass geometry information about the walls +to *dump image* so that the walls will be included in the rendered +image. Please note, that for :doc:`2d systems `, a wall +rendered as a plane would be invisible and it is thus rendered as a +cylinder. Fix *wall/ees/region* does **not** support graphics info, but +the region can be visualized with the *region* keyword of :doc:`dump +image `. + +The color of the wall is by default that of the first atom type when +using color styles "type" or "element". With color style "const" the +default value of "white" can be changed using :doc:`dump_modify fcolor +`. The transparency is by default fully opaque and can be +changed with *dump\_modify ftrans*\ . + +For 2d systems, the *fflag1* setting determines whether the cylinder +representing the wall is capped with a sphere at the ends: 0 means no caps, 1 +means the lower end is capped, 2 means the upper end is capped, and 3 +means both ends are capped. The *fflag2* setting allows to adjust the +radius of the rendered cylinder. It should be set to a value > 0 or the +cylinder will not be visible since the diameter is set internally to +zero due to lack of a suitable heuristic for deriving a meaningful +diameter for all types of walls and unit settings. + +For 3d systems, both *fflag1* and *fflag2* are ignored. + +------------ + Restrictions """""""""""" diff --git a/doc/src/fix_wall_gran.rst b/doc/src/fix_wall_gran.rst index 2dfca7fb3d9..eb194af6d65 100644 --- a/doc/src/fix_wall_gran.rst +++ b/doc/src/fix_wall_gran.rst @@ -276,18 +276,19 @@ in the rendered image. Please note, that for :doc:`2d systems `, a wall rendered as a plane would be invisible and it is thus rendered as a cylinder. +The color of the wall is by default that of the first atom type when +using color styles "type" or "element". With color style "const" the +default value of "white" can be changed using :doc:`dump_modify fcolor +`. The transparency is by default fully opaque and can be +changed globally with *dump\_modify ftrans*\ . + For 2d systems, the *fflag1* setting determines whether the cylinder representing the wall is capped with a sphere at the ends: 0 means no caps, 1 means the lower end is capped, 2 means the upper end is capped, and 3 -means both ends are capped. The *fflag2* setting allows to adjust the -radius of the rendered cylinder. It should be set to a value > 0 or the -cylinder will not be visible since the diameter is set internally to -zero due to lack of a suitable heuristic for deriving a meaningful -diameter for all types of walls and unit settings. - -For 3d systems, the *fflag1* setting is ignored, but the *fflag2* -setting determines the transparency of the wall. It must be set to a -value between 0.0 (invisible) and 1.0 (fully opaque). +means both ends are capped. The *fflag2* setting allows to set the +radius of the rendered cylinders. + +For 3d systems, both *fflag1* and *fflag2* are ignored. ----------------- diff --git a/doc/src/fix_wall_gran_region.rst b/doc/src/fix_wall_gran_region.rst index 4ad3b9d6c52..03fec0a65ba 100644 --- a/doc/src/fix_wall_gran_region.rst +++ b/doc/src/fix_wall_gran_region.rst @@ -282,6 +282,14 @@ No parameter of this fix can be used with the *start/stop* keywords of the :doc:`run ` command. This fix is not invoked during :doc:`energy minimization `. +Dump image info +""""""""""""""" + +This fix does **not** support the *fix* keyword of the :doc:`dump image +` command. Instead the region used by the fix can be +visualized using the *region* keyword of *dump image*. + + Restrictions """""""""""" diff --git a/doc/src/fix_wall_reflect.rst b/doc/src/fix_wall_reflect.rst index 01492701772..8673354e4e3 100644 --- a/doc/src/fix_wall_reflect.rst +++ b/doc/src/fix_wall_reflect.rst @@ -151,6 +151,13 @@ image. Please note, that for :doc:`2d systems `, a wall rendered as a plane would be invisible and it is thus rendered as a cylinder. +The color of the wall is by default that of the first atom type when +using color styles "type" or "element". With color style "const" the +default value of "white" can be changed using :doc:`dump_modify fcolor +`. The transparency is by default fully opaque and can be +changed globally with *dump\_modify ftrans*\ . + + For 2d systems, the *fflag1* setting determines whether the cylinder representing the wall is capped with a sphere at the ends: 0 means no caps, 1 means the lower end is capped, 2 means the upper end is capped, and 3 @@ -160,9 +167,7 @@ cylinder will not be visible since the diameter is set internally to zero due to lack of a suitable heuristic for deriving a meaningful diameter for all types of walls and unit settings. -For 3d systems, the *fflag1* setting is ignored, but the *fflag2* -setting determines the transparency of the wall. It must be set to a -value between 0.0 (invisible) and 1.0 (fully opaque). +For 3d systems, both *fflag1* and *fflag2* are ignored. ---------- diff --git a/doc/src/fix_wall_reflect_stochastic.rst b/doc/src/fix_wall_reflect_stochastic.rst index 3f904ca98e3..55e4bd97077 100644 --- a/doc/src/fix_wall_reflect_stochastic.rst +++ b/doc/src/fix_wall_reflect_stochastic.rst @@ -109,6 +109,12 @@ image. Please note, that for :doc:`2d systems `, a wall rendered as a plane would be invisible and it is thus rendered as a cylinder. +The color of the wall is by default that of the first atom type when +using color styles "type" or "element". With color style "const" the +default value of "white" can be changed using :doc:`dump_modify fcolor +`. The transparency is by default fully opaque and can be +changed with *dump\_modify ftrans*\ . + The *fflag1* setting and the *fflag2* setting of *dump image fix* are only relevant for 2d systems. The *fflag1* setting determines whether the cylinder is capped with a sphere at the ends: 0 means no caps, 1 diff --git a/doc/src/fix_wall_region.rst b/doc/src/fix_wall_region.rst index ba75755ab0b..d53d336f75e 100644 --- a/doc/src/fix_wall_region.rst +++ b/doc/src/fix_wall_region.rst @@ -237,6 +237,13 @@ invoked by the :doc:`minimize ` command. minimized), you MUST enable the :doc:`fix_modify ` *energy* option for this fix. +Dump image info +""""""""""""""" + +This fix does **not** support the *fix* keyword of the :doc:`dump image +` command. Instead the region used by the fix can be +visualized using the *region* keyword of *dump image*. + ---------- .. include:: accel_styles.rst diff --git a/doc/src/fix_wall_srd.rst b/doc/src/fix_wall_srd.rst index dfb6560ffd6..51cb36efcca 100644 --- a/doc/src/fix_wall_srd.rst +++ b/doc/src/fix_wall_srd.rst @@ -58,12 +58,12 @@ A particle/wall collision occurs if an SRD particle moves outside the wall on a timestep. This alters the position and velocity of the SRD particle and imparts a force to the wall. -The *collision* and *Tsrd* settings specified via the :doc:`fix srd ` command affect the SRD/wall collisions. A *slip* -setting for the *collision* keyword means that the tangential -component of the SRD particle momentum is preserved. Thus only a -normal force is imparted to the wall. The normal component of the new -SRD velocity is sampled from a Gaussian distribution at temperature -*Tsrd*\ . +The *collision* and *Tsrd* settings specified via the :doc:`fix srd +` command affect the SRD/wall collisions. A *slip* setting for +the *collision* keyword means that the tangential component of the SRD +particle momentum is preserved. Thus only a normal force is imparted to +the wall. The normal component of the new SRD velocity is sampled from +a Gaussian distribution at temperature *Tsrd*\ . For a *noslip* setting of the *collision* keyword, both the normal and tangential components of the new SRD velocity are sampled from a @@ -175,6 +175,37 @@ perturbation on the particles: position = c0 + A (1 - cos(omega\*delta)) +----------------- + +Dump image info +""""""""""""""" + +.. versionadded:: TBD + +Fix *wall/srd* supports the *fix* keyword of :doc:`dump image +`. The fix will pass geometry information about the walls +to *dump image* so that the walls will be included in the rendered +image. Please note, that for :doc:`2d systems `, a wall +rendered as a plane would be invisible and it is thus rendered as a +cylinder. + +The color of the wall is by default that of the first atom type when +using color styles "type" or "element". With color style "const" the +default value of "white" can be changed using :doc:`dump_modify fcolor +`. The transparency is by default fully opaque and can be +changed with *dump\_modify ftrans*\ . + +For 2d systems, the *fflag1* setting determines whether the cylinder +representing the wall is capped with a sphere at the ends: 0 means no +caps, 1 means the lower end is capped, 2 means the upper end is capped, +and 3 means both ends are capped. The *fflag2* setting allows to adjust +the radius of the rendered cylinder. It should be set to a value > 0 or +the cylinder will not be visible since the diameter is set internally to +zero due to lack of a suitable heuristic for deriving a meaningful +diameter for all types of walls and unit settings. + +For 3d systems, both *fflag1* and *fflag2* are ignored. + ---------- Restart, fix_modify, output, run start/stop, minimize info diff --git a/doc/src/img/body-both.png b/doc/src/img/body-both.png new file mode 100644 index 00000000000..7f538c4e805 Binary files /dev/null and b/doc/src/img/body-both.png differ diff --git a/doc/src/img/body-faces.png b/doc/src/img/body-faces.png new file mode 100644 index 00000000000..a5d0123581d Binary files /dev/null and b/doc/src/img/body-faces.png differ diff --git a/doc/src/img/body-frames.png b/doc/src/img/body-frames.png new file mode 100644 index 00000000000..f3358f80e47 Binary files /dev/null and b/doc/src/img/body-frames.png differ diff --git a/doc/src/img/bonds-auto.png b/doc/src/img/bonds-auto.png new file mode 100644 index 00000000000..bdded2f1bdf Binary files /dev/null and b/doc/src/img/bonds-auto.png differ diff --git a/doc/src/img/bonds-create.png b/doc/src/img/bonds-create.png new file mode 100644 index 00000000000..85a1578a4a5 Binary files /dev/null and b/doc/src/img/bonds-create.png differ diff --git a/doc/src/img/bonds-fix.png b/doc/src/img/bonds-fix.png new file mode 100644 index 00000000000..47bf11a6966 Binary files /dev/null and b/doc/src/img/bonds-fix.png differ diff --git a/doc/src/img/bonds-vdw.png b/doc/src/img/bonds-vdw.png new file mode 100644 index 00000000000..ab95d288eff Binary files /dev/null and b/doc/src/img/bonds-vdw.png differ diff --git a/doc/src/img/colors-default.png b/doc/src/img/colors-default.png new file mode 100644 index 00000000000..d389d2be4f0 Binary files /dev/null and b/doc/src/img/colors-default.png differ diff --git a/doc/src/img/colors-element.png b/doc/src/img/colors-element.png new file mode 100644 index 00000000000..c9cf3a2ac61 Binary files /dev/null and b/doc/src/img/colors-element.png differ diff --git a/doc/src/img/colors-map.png b/doc/src/img/colors-map.png new file mode 100644 index 00000000000..c27da58e7d2 Binary files /dev/null and b/doc/src/img/colors-map.png differ diff --git a/doc/src/img/colors-newmap.png b/doc/src/img/colors-newmap.png new file mode 100644 index 00000000000..8448fac83cf Binary files /dev/null and b/doc/src/img/colors-newmap.png differ diff --git a/doc/src/img/dump2.png b/doc/src/img/dump2.png new file mode 100644 index 00000000000..9a1f6093558 Binary files /dev/null and b/doc/src/img/dump2.png differ diff --git a/doc/src/img/dump3.png b/doc/src/img/dump3.png new file mode 100644 index 00000000000..ecd31f2481b Binary files /dev/null and b/doc/src/img/dump3.png differ diff --git a/doc/src/img/dump4.png b/doc/src/img/dump4.png new file mode 100644 index 00000000000..c2e0a8aaf8c Binary files /dev/null and b/doc/src/img/dump4.png differ diff --git a/doc/src/img/dump5.png b/doc/src/img/dump5.png new file mode 100644 index 00000000000..9deeb74ffac Binary files /dev/null and b/doc/src/img/dump5.png differ diff --git a/doc/src/img/ellipsoid-level2.png b/doc/src/img/ellipsoid-level2.png new file mode 100644 index 00000000000..b8f8821ea9f Binary files /dev/null and b/doc/src/img/ellipsoid-level2.png differ diff --git a/doc/src/img/ellipsoid-level4.png b/doc/src/img/ellipsoid-level4.png new file mode 100644 index 00000000000..5b0d7733f7a Binary files /dev/null and b/doc/src/img/ellipsoid-level4.png differ diff --git a/doc/src/img/ellipsoid-level6.png b/doc/src/img/ellipsoid-level6.png new file mode 100644 index 00000000000..d87ac87fedd Binary files /dev/null and b/doc/src/img/ellipsoid-level6.png differ diff --git a/doc/src/img/fix-arrows.png b/doc/src/img/fix-arrows.png new file mode 100644 index 00000000000..6ba6dee3655 Binary files /dev/null and b/doc/src/img/fix-arrows.png differ diff --git a/doc/src/img/fix-dipoles.png b/doc/src/img/fix-dipoles.png new file mode 100644 index 00000000000..f4e16090ea3 Binary files /dev/null and b/doc/src/img/fix-dipoles.png differ diff --git a/doc/src/img/fix-graphics.png b/doc/src/img/fix-graphics.png new file mode 100644 index 00000000000..d4967745e01 Binary files /dev/null and b/doc/src/img/fix-graphics.png differ diff --git a/doc/src/img/fix-indent.png b/doc/src/img/fix-indent.png new file mode 100644 index 00000000000..530af671897 Binary files /dev/null and b/doc/src/img/fix-indent.png differ diff --git a/doc/src/img/fix-mesh.png b/doc/src/img/fix-mesh.png new file mode 100644 index 00000000000..955234759fa Binary files /dev/null and b/doc/src/img/fix-mesh.png differ diff --git a/doc/src/img/isosurface-lammps.png b/doc/src/img/isosurface-lammps.png new file mode 100644 index 00000000000..ecacc3df717 Binary files /dev/null and b/doc/src/img/isosurface-lammps.png differ diff --git a/doc/src/img/isosurface-opengl.png b/doc/src/img/isosurface-opengl.png new file mode 100644 index 00000000000..a52a560fb81 Binary files /dev/null and b/doc/src/img/isosurface-opengl.png differ diff --git a/doc/src/img/isosurface-raytrace.png b/doc/src/img/isosurface-raytrace.png new file mode 100644 index 00000000000..6429a8b577e Binary files /dev/null and b/doc/src/img/isosurface-raytrace.png differ diff --git a/doc/src/img/region-boxes.png b/doc/src/img/region-boxes.png new file mode 100644 index 00000000000..c34aeaf31ce Binary files /dev/null and b/doc/src/img/region-boxes.png differ diff --git a/doc/src/img/region-cones.png b/doc/src/img/region-cones.png new file mode 100644 index 00000000000..016ea754cdd Binary files /dev/null and b/doc/src/img/region-cones.png differ diff --git a/doc/src/img/region-plane.png b/doc/src/img/region-plane.png new file mode 100644 index 00000000000..a97579e88ec Binary files /dev/null and b/doc/src/img/region-plane.png differ diff --git a/doc/src/img/rhodo-all.png b/doc/src/img/rhodo-all.png new file mode 100644 index 00000000000..5dbf8613698 Binary files /dev/null and b/doc/src/img/rhodo-all.png differ diff --git a/doc/src/img/rhodo-iso.png b/doc/src/img/rhodo-iso.png new file mode 100644 index 00000000000..13f6b6d70a3 Binary files /dev/null and b/doc/src/img/rhodo-iso.png differ diff --git a/doc/src/img/transparent-1.png b/doc/src/img/transparent-1.png new file mode 100644 index 00000000000..627d276f163 Binary files /dev/null and b/doc/src/img/transparent-1.png differ diff --git a/doc/src/img/transparent-2.png b/doc/src/img/transparent-2.png new file mode 100644 index 00000000000..186f7c4b6e6 Binary files /dev/null and b/doc/src/img/transparent-2.png differ diff --git a/doc/src/package.rst b/doc/src/package.rst index 1c3ced7cf67..eefecac4fd4 100644 --- a/doc/src/package.rst +++ b/doc/src/package.rst @@ -4,6 +4,8 @@ package command =============== .. contents:: + :local: + :backlinks: top Syntax """""" diff --git a/doc/src/pair_wf_cut.rst b/doc/src/pair_wf_cut.rst index c3c0c0baf97..9e2eb67a1f5 100644 --- a/doc/src/pair_wf_cut.rst +++ b/doc/src/pair_wf_cut.rst @@ -44,7 +44,7 @@ with and .. math:: - r_{min}=r_c\left[\frac{1+2\nu}{1+2\nu(r_c/\sigma)^{2\nu}}\right]^{1/{2\nu}} + r_{min}=r_c\left[\frac{1+2\nu}{1+2\nu(r_c/\sigma)^{2\nu}}\right]^{1/{2\mu}} :math:`r_c` is the cutoff. diff --git a/doc/src/python.rst b/doc/src/python.rst index bdde9c9a789..fa3de01c590 100644 --- a/doc/src/python.rst +++ b/doc/src/python.rst @@ -736,11 +736,6 @@ This command is part of the PYTHON package. It is only enabled if LAMMPS was built with that package. See the :doc:`Build package ` page for more info. -Building LAMMPS with the PYTHON package will link LAMMPS with the Python -library on your system. Settings to enable this are in the -lib/python/Makefile.lammps file. See the lib/python/README file for -information on those settings. - If you use Python code which calls back to LAMMPS, via the SELF input argument explained above, there is an extra step required when building LAMMPS. LAMMPS must also be built as a shared library and your Python diff --git a/doc/src/region2vmd.rst b/doc/src/region2vmd.rst index f403c9c6ba4..b03ce18d492 100644 --- a/doc/src/region2vmd.rst +++ b/doc/src/region2vmd.rst @@ -44,6 +44,13 @@ molecule" and then assigning a sequence of VMD graphics primitives to represent the region in VMD. Each region will be stored in a separate "VMD molecule" with the name "LAMMPS region ". +.. admonition:: Visualization of regions with *dump image* + :class: Hint + + Regions can also be directly visualized within LAMMPS using the + :doc:`dump image ` command. For more information, + see :doc:`Howto_viz`. + The *region2vmd* command is following by the filename for the resulting VMD script and an arbitrary number of keyword argument pairs to either write out a new *region* visualization, change the *color* or *material* @@ -158,8 +165,10 @@ file>', or from the File menu via "Load VMD visualization state". Restrictions """""""""""" -This command is part of the EXTRA-COMMAND package. It is only enabled -if LAMMPS was built with that package. See the :doc:`Build package +.. versionchanged:: TBD + +This command is part of the GRAPHICS package. It is only enabled if +LAMMPS was built with that package. See the :doc:`Build package ` page for more info. Only the following region styles are currently supported: *block*, @@ -171,7 +180,7 @@ Rotating regions are currently not supported. Related commands """""""""""""""" -:doc:`region ` +:doc:`region `, :doc:`dump image ` Defaults """""""" diff --git a/doc/utils/sphinx-config/LAMMPSLexer.py b/doc/utils/sphinx-config/LAMMPSLexer.py index b28a04be39f..1a22fb14743 100644 --- a/doc/utils/sphinx-config/LAMMPSLexer.py +++ b/doc/utils/sphinx-config/LAMMPSLexer.py @@ -68,6 +68,7 @@ class LAMMPSLexer(RegexLexer): include('conditionals'), include('keywords'), (r'#.*?\n', Comment), + (r' &\n', Literal.String.Char), (r'"', String, 'string'), (r'\'', String, 'single_quote_string'), (r'[0-9]+:[0-9]+(:[0-9]+)?', Number), diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index f2cc6c1aed2..17b423cc213 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -202,6 +202,7 @@ atype augt AuO automagically +autoscale Auvergne Avalos avalue @@ -313,6 +314,7 @@ bigint Bij bilayer bilayers +bilinear binsize binstyle binutils @@ -1282,6 +1284,7 @@ Fock Fogarty Foiles fontawesome +fontcolor fopenmp forceclear forestgreen @@ -1301,6 +1304,7 @@ fplo fprintf Fqq Fraige +framecolor framerate Frauenheim Fraunhofer @@ -1738,6 +1742,8 @@ isoenergetic isoenthalpic isokinetic isomorphism +isosurface +isosurfaces isothermal isotropically isovalue @@ -2831,6 +2837,7 @@ obabo ochre ocl octahedral +octahedron octants Odegard Og @@ -3044,6 +3051,8 @@ Pisarev Pishevar Pitera pitorsion +pixmap +pixmaps pj pjintve pKa @@ -3276,6 +3285,7 @@ RANN Rapetti Raphson Rappe +rasterizer Ravelo rc Rc @@ -3974,6 +3984,7 @@ tradeoff traj Tranchida transactional +transcolor transferability translational Translational @@ -4289,6 +4300,8 @@ wildcard Wildcard wildcards Winkler +wireframe +wireframes Wirnsberger wirtes witin diff --git a/examples/GRAPHICS/CH.airebo b/examples/GRAPHICS/CH.airebo new file mode 120000 index 00000000000..60249102a52 --- /dev/null +++ b/examples/GRAPHICS/CH.airebo @@ -0,0 +1 @@ +../../potentials/CH.airebo \ No newline at end of file diff --git a/examples/GRAPHICS/README b/examples/GRAPHICS/README new file mode 100644 index 00000000000..448fa166e77 --- /dev/null +++ b/examples/GRAPHICS/README @@ -0,0 +1,14 @@ +Examples for advanced visualizations with dump image in LAMMPS + +This folder contains complete input examples for creating visualizations +with the dump image command and various fixes in the GRAPHICS package. +For exploring different options and settings, it is recommended to run +these examples from LAMMPS-GUI and (temporarily) disable FSAA and SSAO. +The simulation can then be conveniently started with CTRL-Enter and +stopped with CTRL-\ and the slideshow viewer will display the images +automatically as they are created. + +Here is a list of the inputs and which graphics features they demonstrate + +- in.cubes-and-pyramids: visualization of body particles +- in.breakable: using autobond, color-by-property, adding objects and text diff --git a/examples/GRAPHICS/data.breakable b/examples/GRAPHICS/data.breakable new file mode 100644 index 00000000000..7dd41d25e6f --- /dev/null +++ b/examples/GRAPHICS/data.breakable @@ -0,0 +1,713 @@ +LAMMPS data file. + 700 atoms + 1 atom types + -60 60 xlo xhi + -40 40 ylo yhi + -40 40 zlo zhi + + Masses + + 1 12.0107 + + Atoms # atomic + +1 1 -19.026 0.465 -5.162 +2 1 -18.758 1.821 -4.853 +3 1 -17.418 2.249 -4.67 +4 1 -17.151 3.41 -3.903 +5 1 -21.438 0.925 -5.1 +6 1 -21.17 2.249 -4.67 +7 1 -19.83 2.658 -4.45 +8 1 -19.562 3.746 -3.582 +9 1 -18.222 4.052 -3.232 +10 1 -17.954 4.766 -2.037 +11 1 -23.85 1.379 -4.996 +12 1 -23.582 2.658 -4.45 +13 1 -22.242 3.047 -4.193 +14 1 -21.974 4.052 -3.232 +15 1 -20.634 4.326 -2.855 +16 1 -20.366 4.93 -1.602 +17 1 -19.026 5.053 -1.153 +18 1 -18.758 5.178 0.233 +19 1 -17.418 5.136 0.696 +20 1 -17.151 4.766 2.037 +21 1 -27.87 0.0 -5.183 +22 1 -27.602 1.379 -4.996 +23 1 -26.262 1.821 -4.853 +24 1 -25.994 3.047 -4.193 +25 1 -24.654 3.41 -3.903 +26 1 -24.386 4.326 -2.855 +27 1 -23.046 4.564 -2.456 +28 1 -22.778 5.053 -1.153 +29 1 -21.438 5.136 -0.696 +30 1 -21.17 5.136 0.696 +31 1 -19.83 5.053 1.153 +32 1 -19.562 4.564 2.456 +33 1 -18.222 4.326 2.855 +34 1 -17.954 3.41 3.903 +35 1 -27.066 3.746 -3.582 +36 1 -26.798 4.564 -2.456 +37 1 -25.458 4.766 -2.037 +38 1 -25.19 5.136 -0.696 +39 1 -23.85 5.178 -0.233 +40 1 -23.582 5.053 1.153 +41 1 -22.242 4.93 1.602 +42 1 -21.974 4.326 2.855 +43 1 -20.634 4.052 3.232 +44 1 -20.366 3.047 4.193 +45 1 -19.026 2.658 4.45 +46 1 -18.758 1.379 4.996 +47 1 -17.418 0.925 5.1 +48 1 -17.151 -0.465 5.162 +49 1 -27.87 4.93 -1.602 +50 1 -27.602 5.178 -0.233 +51 1 -26.262 5.178 0.233 +52 1 -25.994 4.93 1.602 +53 1 -24.654 4.766 2.037 +54 1 -24.386 4.052 3.232 +55 1 -23.046 3.746 3.582 +56 1 -22.778 2.658 4.45 +57 1 -21.438 2.249 4.67 +58 1 -21.17 0.925 5.1 +59 1 -19.83 0.465 5.162 +60 1 -19.562 -0.925 5.1 +61 1 -18.222 -1.379 4.996 +62 1 -17.954 -2.658 4.45 +63 1 -27.066 4.564 2.456 +64 1 -26.798 3.746 3.582 +65 1 -25.458 3.41 3.903 +66 1 -25.19 2.249 4.67 +67 1 -23.85 1.821 4.853 +68 1 -23.582 0.465 5.162 +69 1 -22.242 0.0 5.183 +70 1 -21.974 -1.379 4.996 +71 1 -20.634 -1.821 4.853 +72 1 -20.366 -3.047 4.193 +73 1 -19.026 -3.41 3.903 +74 1 -18.758 -4.326 2.855 +75 1 -17.418 -4.564 2.456 +76 1 -17.151 -5.053 1.153 +77 1 -27.87 3.047 4.193 +78 1 -27.602 1.821 4.853 +79 1 -26.262 1.379 4.996 +80 1 -25.994 0.0 5.183 +81 1 -24.654 -0.465 5.162 +82 1 -24.386 -1.821 4.853 +83 1 -23.046 -2.249 4.67 +84 1 -22.778 -3.41 3.903 +85 1 -21.438 -3.746 3.582 +86 1 -21.17 -4.564 2.456 +87 1 -19.83 -4.766 2.037 +88 1 -19.562 -5.136 0.696 +89 1 -18.222 -5.178 0.233 +90 1 -17.954 -5.053 -1.153 +91 1 -27.066 -0.925 5.1 +92 1 -26.798 -2.249 4.67 +93 1 -25.458 -2.658 4.45 +94 1 -25.19 -3.746 3.582 +95 1 -23.85 -4.052 3.232 +96 1 -23.582 -4.766 2.037 +97 1 -22.242 -4.93 1.602 +98 1 -21.974 -5.178 0.233 +99 1 -20.634 -5.178 -0.233 +100 1 -20.366 -4.93 -1.602 +101 1 -19.026 -4.766 -2.037 +102 1 -18.758 -4.052 -3.232 +103 1 -17.418 -3.746 -3.582 +104 1 -17.151 -2.658 -4.45 +105 1 -27.87 -3.047 4.193 +106 1 -27.602 -4.052 3.232 +107 1 -26.262 -4.326 2.855 +108 1 -25.994 -4.93 1.602 +109 1 -24.654 -5.053 1.153 +110 1 -24.386 -5.178 -0.233 +111 1 -23.046 -5.136 -0.696 +112 1 -22.778 -4.766 -2.037 +113 1 -21.438 -4.564 -2.456 +114 1 -21.17 -3.746 -3.582 +115 1 -19.83 -3.41 -3.903 +116 1 -19.562 -2.249 -4.67 +117 1 -18.222 -1.821 -4.853 +118 1 -17.954 -0.465 -5.162 +119 1 -27.066 -5.136 0.696 +120 1 -26.798 -5.136 -0.696 +121 1 -25.458 -5.053 -1.153 +122 1 -25.19 -4.564 -2.456 +123 1 -23.85 -4.326 -2.855 +124 1 -23.582 -3.41 -3.903 +125 1 -22.242 -3.047 -4.193 +126 1 -21.974 -1.821 -4.853 +127 1 -20.634 -1.379 -4.996 +128 1 -20.366 0.0 -5.183 +129 1 -27.87 -4.93 -1.602 +130 1 -27.602 -4.326 -2.855 +131 1 -26.262 -4.052 -3.232 +132 1 -25.994 -3.047 -4.193 +133 1 -24.654 -2.658 -4.45 +134 1 -24.386 -1.379 -4.996 +135 1 -23.046 -0.925 -5.1 +136 1 -22.778 0.465 -5.162 +137 1 -27.066 -2.249 -4.67 +138 1 -26.798 -0.925 -5.1 +139 1 -25.458 -0.465 -5.162 +140 1 -25.19 0.925 -5.1 +141 1 -7.771 0.465 -5.162 +142 1 -7.503 1.821 -4.853 +143 1 -6.163 2.249 -4.67 +144 1 -5.895 3.41 -3.903 +145 1 -10.183 0.925 -5.1 +146 1 -9.915 2.249 -4.67 +147 1 -8.575 2.658 -4.45 +148 1 -8.307 3.746 -3.582 +149 1 -6.967 4.052 -3.232 +150 1 -6.699 4.766 -2.037 +151 1 -12.595 1.379 -4.996 +152 1 -12.327 2.658 -4.45 +153 1 -10.987 3.047 -4.193 +154 1 -10.719 4.052 -3.232 +155 1 -9.379 4.326 -2.855 +156 1 -9.111 4.93 -1.602 +157 1 -7.771 5.053 -1.153 +158 1 -7.503 5.178 0.233 +159 1 -6.163 5.136 0.696 +160 1 -5.895 4.766 2.037 +161 1 -16.615 0.0 -5.183 +162 1 -16.347 1.379 -4.996 +163 1 -15.007 1.821 -4.853 +164 1 -14.739 3.047 -4.193 +165 1 -13.399 3.41 -3.903 +166 1 -13.131 4.326 -2.855 +167 1 -11.791 4.564 -2.456 +168 1 -11.523 5.053 -1.153 +169 1 -10.183 5.136 -0.696 +170 1 -9.915 5.136 0.696 +171 1 -8.575 5.053 1.153 +172 1 -8.307 4.564 2.456 +173 1 -6.967 4.326 2.855 +174 1 -6.699 3.41 3.903 +175 1 -15.811 3.746 -3.582 +176 1 -15.543 4.564 -2.456 +177 1 -14.203 4.766 -2.037 +178 1 -13.935 5.136 -0.696 +179 1 -12.595 5.178 -0.233 +180 1 -12.327 5.053 1.153 +181 1 -10.987 4.93 1.602 +182 1 -10.719 4.326 2.855 +183 1 -9.379 4.052 3.232 +184 1 -9.111 3.047 4.193 +185 1 -7.771 2.658 4.45 +186 1 -7.503 1.379 4.996 +187 1 -6.163 0.925 5.1 +188 1 -5.895 -0.465 5.162 +189 1 -16.615 4.93 -1.602 +190 1 -16.347 5.178 -0.233 +191 1 -15.007 5.178 0.233 +192 1 -14.739 4.93 1.602 +193 1 -13.399 4.766 2.037 +194 1 -13.131 4.052 3.232 +195 1 -11.791 3.746 3.582 +196 1 -11.523 2.658 4.45 +197 1 -10.183 2.249 4.67 +198 1 -9.915 0.925 5.1 +199 1 -8.575 0.465 5.162 +200 1 -8.307 -0.925 5.1 +201 1 -6.967 -1.379 4.996 +202 1 -6.699 -2.658 4.45 +203 1 -15.811 4.564 2.456 +204 1 -15.543 3.746 3.582 +205 1 -14.203 3.41 3.903 +206 1 -13.935 2.249 4.67 +207 1 -12.595 1.821 4.853 +208 1 -12.327 0.465 5.162 +209 1 -10.987 0.0 5.183 +210 1 -10.719 -1.379 4.996 +211 1 -9.379 -1.821 4.853 +212 1 -9.111 -3.047 4.193 +213 1 -7.771 -3.41 3.903 +214 1 -7.503 -4.326 2.855 +215 1 -6.163 -4.564 2.456 +216 1 -5.895 -5.053 1.153 +217 1 -16.615 3.047 4.193 +218 1 -16.347 1.821 4.853 +219 1 -15.007 1.379 4.996 +220 1 -14.739 0.0 5.183 +221 1 -13.399 -0.465 5.162 +222 1 -13.131 -1.821 4.853 +223 1 -11.791 -2.249 4.67 +224 1 -11.523 -3.41 3.903 +225 1 -10.183 -3.746 3.582 +226 1 -9.915 -4.564 2.456 +227 1 -8.575 -4.766 2.037 +228 1 -8.307 -5.136 0.696 +229 1 -6.967 -5.178 0.233 +230 1 -6.699 -5.053 -1.153 +231 1 -15.811 -0.925 5.1 +232 1 -15.543 -2.249 4.67 +233 1 -14.203 -2.658 4.45 +234 1 -13.935 -3.746 3.582 +235 1 -12.595 -4.052 3.232 +236 1 -12.327 -4.766 2.037 +237 1 -10.987 -4.93 1.602 +238 1 -10.719 -5.178 0.233 +239 1 -9.379 -5.178 -0.233 +240 1 -9.111 -4.93 -1.602 +241 1 -7.771 -4.766 -2.037 +242 1 -7.503 -4.052 -3.232 +243 1 -6.163 -3.746 -3.582 +244 1 -5.895 -2.658 -4.45 +245 1 -16.615 -3.047 4.193 +246 1 -16.347 -4.052 3.232 +247 1 -15.007 -4.326 2.855 +248 1 -14.739 -4.93 1.602 +249 1 -13.399 -5.053 1.153 +250 1 -13.131 -5.178 -0.233 +251 1 -11.791 -5.136 -0.696 +252 1 -11.523 -4.766 -2.037 +253 1 -10.183 -4.564 -2.456 +254 1 -9.915 -3.746 -3.582 +255 1 -8.575 -3.41 -3.903 +256 1 -8.307 -2.249 -4.67 +257 1 -6.967 -1.821 -4.853 +258 1 -6.699 -0.465 -5.162 +259 1 -15.811 -5.136 0.696 +260 1 -15.543 -5.136 -0.696 +261 1 -14.203 -5.053 -1.153 +262 1 -13.935 -4.564 -2.456 +263 1 -12.595 -4.326 -2.855 +264 1 -12.327 -3.41 -3.903 +265 1 -10.987 -3.047 -4.193 +266 1 -10.719 -1.821 -4.853 +267 1 -9.379 -1.379 -4.996 +268 1 -9.111 0.0 -5.183 +269 1 -16.615 -4.93 -1.602 +270 1 -16.347 -4.326 -2.855 +271 1 -15.007 -4.052 -3.232 +272 1 -14.739 -3.047 -4.193 +273 1 -13.399 -2.658 -4.45 +274 1 -13.131 -1.379 -4.996 +275 1 -11.791 -0.925 -5.1 +276 1 -11.523 0.465 -5.162 +277 1 -15.811 -2.249 -4.67 +278 1 -15.543 -0.925 -5.1 +279 1 -14.203 -0.465 -5.162 +280 1 -13.935 0.925 -5.1 +281 1 3.484 0.465 -5.162 +282 1 3.752 1.821 -4.853 +283 1 5.092 2.249 -4.67 +284 1 5.36 3.41 -3.903 +285 1 1.072 0.925 -5.1 +286 1 1.34 2.249 -4.67 +287 1 2.68 2.658 -4.45 +288 1 2.948 3.746 -3.582 +289 1 4.288 4.052 -3.232 +290 1 4.556 4.766 -2.037 +291 1 -1.34 1.379 -4.996 +292 1 -1.072 2.658 -4.45 +293 1 0.268 3.047 -4.193 +294 1 0.536 4.052 -3.232 +295 1 1.876 4.326 -2.855 +296 1 2.144 4.93 -1.602 +297 1 3.484 5.053 -1.153 +298 1 3.752 5.178 0.233 +299 1 5.092 5.136 0.696 +300 1 5.36 4.766 2.037 +301 1 -5.36 0.0 -5.183 +302 1 -5.092 1.379 -4.996 +303 1 -3.752 1.821 -4.853 +304 1 -3.484 3.047 -4.193 +305 1 -2.144 3.41 -3.903 +306 1 -1.876 4.326 -2.855 +307 1 -0.536 4.564 -2.456 +308 1 -0.268 5.053 -1.153 +309 1 1.072 5.136 -0.696 +310 1 1.34 5.136 0.696 +311 1 2.68 5.053 1.153 +312 1 2.948 4.564 2.456 +313 1 4.288 4.326 2.855 +314 1 4.556 3.41 3.903 +315 1 -4.556 3.746 -3.582 +316 1 -4.288 4.564 -2.456 +317 1 -2.948 4.766 -2.037 +318 1 -2.68 5.136 -0.696 +319 1 -1.34 5.178 -0.233 +320 1 -1.072 5.053 1.153 +321 1 0.268 4.93 1.602 +322 1 0.536 4.326 2.855 +323 1 1.876 4.052 3.232 +324 1 2.144 3.047 4.193 +325 1 3.484 2.658 4.45 +326 1 3.752 1.379 4.996 +327 1 5.092 0.925 5.1 +328 1 5.36 -0.465 5.162 +329 1 -5.36 4.93 -1.602 +330 1 -5.092 5.178 -0.233 +331 1 -3.752 5.178 0.233 +332 1 -3.484 4.93 1.602 +333 1 -2.144 4.766 2.037 +334 1 -1.876 4.052 3.232 +335 1 -0.536 3.746 3.582 +336 1 -0.268 2.658 4.45 +337 1 1.072 2.249 4.67 +338 1 1.34 0.925 5.1 +339 1 2.68 0.465 5.162 +340 1 2.948 -0.925 5.1 +341 1 4.288 -1.379 4.996 +342 1 4.556 -2.658 4.45 +343 1 -4.556 4.564 2.456 +344 1 -4.288 3.746 3.582 +345 1 -2.948 3.41 3.903 +346 1 -2.68 2.249 4.67 +347 1 -1.34 1.821 4.853 +348 1 -1.072 0.465 5.162 +349 1 0.268 0.0 5.183 +350 1 0.536 -1.379 4.996 +351 1 1.876 -1.821 4.853 +352 1 2.144 -3.047 4.193 +353 1 3.484 -3.41 3.903 +354 1 3.752 -4.326 2.855 +355 1 5.092 -4.564 2.456 +356 1 5.36 -5.053 1.153 +357 1 -5.36 3.047 4.193 +358 1 -5.092 1.821 4.853 +359 1 -3.752 1.379 4.996 +360 1 -3.484 0.0 5.183 +361 1 -2.144 -0.465 5.162 +362 1 -1.876 -1.821 4.853 +363 1 -0.536 -2.249 4.67 +364 1 -0.268 -3.41 3.903 +365 1 1.072 -3.746 3.582 +366 1 1.34 -4.564 2.456 +367 1 2.68 -4.766 2.037 +368 1 2.948 -5.136 0.696 +369 1 4.288 -5.178 0.233 +370 1 4.556 -5.053 -1.153 +371 1 -4.556 -0.925 5.1 +372 1 -4.288 -2.249 4.67 +373 1 -2.948 -2.658 4.45 +374 1 -2.68 -3.746 3.582 +375 1 -1.34 -4.052 3.232 +376 1 -1.072 -4.766 2.037 +377 1 0.268 -4.93 1.602 +378 1 0.536 -5.178 0.233 +379 1 1.876 -5.178 -0.233 +380 1 2.144 -4.93 -1.602 +381 1 3.484 -4.766 -2.037 +382 1 3.752 -4.052 -3.232 +383 1 5.092 -3.746 -3.582 +384 1 5.36 -2.658 -4.45 +385 1 -5.36 -3.047 4.193 +386 1 -5.092 -4.052 3.232 +387 1 -3.752 -4.326 2.855 +388 1 -3.484 -4.93 1.602 +389 1 -2.144 -5.053 1.153 +390 1 -1.876 -5.178 -0.233 +391 1 -0.536 -5.136 -0.696 +392 1 -0.268 -4.766 -2.037 +393 1 1.072 -4.564 -2.456 +394 1 1.34 -3.746 -3.582 +395 1 2.68 -3.41 -3.903 +396 1 2.948 -2.249 -4.67 +397 1 4.288 -1.821 -4.853 +398 1 4.556 -0.465 -5.162 +399 1 -4.556 -5.136 0.696 +400 1 -4.288 -5.136 -0.696 +401 1 -2.948 -5.053 -1.153 +402 1 -2.68 -4.564 -2.456 +403 1 -1.34 -4.326 -2.855 +404 1 -1.072 -3.41 -3.903 +405 1 0.268 -3.047 -4.193 +406 1 0.536 -1.821 -4.853 +407 1 1.876 -1.379 -4.996 +408 1 2.144 0.0 -5.183 +409 1 -5.36 -4.93 -1.602 +410 1 -5.092 -4.326 -2.855 +411 1 -3.752 -4.052 -3.232 +412 1 -3.484 -3.047 -4.193 +413 1 -2.144 -2.658 -4.45 +414 1 -1.876 -1.379 -4.996 +415 1 -0.536 -0.925 -5.1 +416 1 -0.268 0.465 -5.162 +417 1 -4.556 -2.249 -4.67 +418 1 -4.288 -0.925 -5.1 +419 1 -2.948 -0.465 -5.162 +420 1 -2.68 0.925 -5.1 +421 1 14.739 0.465 -5.162 +422 1 15.007 1.821 -4.853 +423 1 16.347 2.249 -4.67 +424 1 16.615 3.41 -3.903 +425 1 12.327 0.925 -5.1 +426 1 12.595 2.249 -4.67 +427 1 13.935 2.658 -4.45 +428 1 14.203 3.746 -3.582 +429 1 15.543 4.052 -3.232 +430 1 15.811 4.766 -2.037 +431 1 9.915 1.379 -4.996 +432 1 10.183 2.658 -4.45 +433 1 11.523 3.047 -4.193 +434 1 11.791 4.052 -3.232 +435 1 13.131 4.326 -2.855 +436 1 13.399 4.93 -1.602 +437 1 14.739 5.053 -1.153 +438 1 15.007 5.178 0.233 +439 1 16.347 5.136 0.696 +440 1 16.615 4.766 2.037 +441 1 5.895 0.0 -5.183 +442 1 6.163 1.379 -4.996 +443 1 7.503 1.821 -4.853 +444 1 7.771 3.047 -4.193 +445 1 9.111 3.41 -3.903 +446 1 9.379 4.326 -2.855 +447 1 10.719 4.564 -2.456 +448 1 10.987 5.053 -1.153 +449 1 12.327 5.136 -0.696 +450 1 12.595 5.136 0.696 +451 1 13.935 5.053 1.153 +452 1 14.203 4.564 2.456 +453 1 15.543 4.326 2.855 +454 1 15.811 3.41 3.903 +455 1 6.699 3.746 -3.582 +456 1 6.967 4.564 -2.456 +457 1 8.307 4.766 -2.037 +458 1 8.575 5.136 -0.696 +459 1 9.915 5.178 -0.233 +460 1 10.183 5.053 1.153 +461 1 11.523 4.93 1.602 +462 1 11.791 4.326 2.855 +463 1 13.131 4.052 3.232 +464 1 13.399 3.047 4.193 +465 1 14.739 2.658 4.45 +466 1 15.007 1.379 4.996 +467 1 16.347 0.925 5.1 +468 1 16.615 -0.465 5.162 +469 1 5.895 4.93 -1.602 +470 1 6.163 5.178 -0.233 +471 1 7.503 5.178 0.233 +472 1 7.771 4.93 1.602 +473 1 9.111 4.766 2.037 +474 1 9.379 4.052 3.232 +475 1 10.719 3.746 3.582 +476 1 10.987 2.658 4.45 +477 1 12.327 2.249 4.67 +478 1 12.595 0.925 5.1 +479 1 13.935 0.465 5.162 +480 1 14.203 -0.925 5.1 +481 1 15.543 -1.379 4.996 +482 1 15.811 -2.658 4.45 +483 1 6.699 4.564 2.456 +484 1 6.967 3.746 3.582 +485 1 8.307 3.41 3.903 +486 1 8.575 2.249 4.67 +487 1 9.915 1.821 4.853 +488 1 10.183 0.465 5.162 +489 1 11.523 0.0 5.183 +490 1 11.791 -1.379 4.996 +491 1 13.131 -1.821 4.853 +492 1 13.399 -3.047 4.193 +493 1 14.739 -3.41 3.903 +494 1 15.007 -4.326 2.855 +495 1 16.347 -4.564 2.456 +496 1 16.615 -5.053 1.153 +497 1 5.895 3.047 4.193 +498 1 6.163 1.821 4.853 +499 1 7.503 1.379 4.996 +500 1 7.771 0.0 5.183 +501 1 9.111 -0.465 5.162 +502 1 9.379 -1.821 4.853 +503 1 10.719 -2.249 4.67 +504 1 10.987 -3.41 3.903 +505 1 12.327 -3.746 3.582 +506 1 12.595 -4.564 2.456 +507 1 13.935 -4.766 2.037 +508 1 14.203 -5.136 0.696 +509 1 15.543 -5.178 0.233 +510 1 15.811 -5.053 -1.153 +511 1 6.699 -0.925 5.1 +512 1 6.967 -2.249 4.67 +513 1 8.307 -2.658 4.45 +514 1 8.575 -3.746 3.582 +515 1 9.915 -4.052 3.232 +516 1 10.183 -4.766 2.037 +517 1 11.523 -4.93 1.602 +518 1 11.791 -5.178 0.233 +519 1 13.131 -5.178 -0.233 +520 1 13.399 -4.93 -1.602 +521 1 14.739 -4.766 -2.037 +522 1 15.007 -4.052 -3.232 +523 1 16.347 -3.746 -3.582 +524 1 16.615 -2.658 -4.45 +525 1 5.895 -3.047 4.193 +526 1 6.163 -4.052 3.232 +527 1 7.503 -4.326 2.855 +528 1 7.771 -4.93 1.602 +529 1 9.111 -5.053 1.153 +530 1 9.379 -5.178 -0.233 +531 1 10.719 -5.136 -0.696 +532 1 10.987 -4.766 -2.037 +533 1 12.327 -4.564 -2.456 +534 1 12.595 -3.746 -3.582 +535 1 13.935 -3.41 -3.903 +536 1 14.203 -2.249 -4.67 +537 1 15.543 -1.821 -4.853 +538 1 15.811 -0.465 -5.162 +539 1 6.699 -5.136 0.696 +540 1 6.967 -5.136 -0.696 +541 1 8.307 -5.053 -1.153 +542 1 8.575 -4.564 -2.456 +543 1 9.915 -4.326 -2.855 +544 1 10.183 -3.41 -3.903 +545 1 11.523 -3.047 -4.193 +546 1 11.791 -1.821 -4.853 +547 1 13.131 -1.379 -4.996 +548 1 13.399 0.0 -5.183 +549 1 5.895 -4.93 -1.602 +550 1 6.163 -4.326 -2.855 +551 1 7.503 -4.052 -3.232 +552 1 7.771 -3.047 -4.193 +553 1 9.111 -2.658 -4.45 +554 1 9.379 -1.379 -4.996 +555 1 10.719 -0.925 -5.1 +556 1 10.987 0.465 -5.162 +557 1 6.699 -2.249 -4.67 +558 1 6.967 -0.925 -5.1 +559 1 8.307 -0.465 -5.162 +560 1 8.575 0.925 -5.1 +561 1 25.994 0.465 -5.162 +562 1 26.262 1.821 -4.853 +563 1 27.602 2.249 -4.67 +564 1 27.87 3.41 -3.903 +565 1 23.582 0.925 -5.1 +566 1 23.85 2.249 -4.67 +567 1 25.19 2.658 -4.45 +568 1 25.458 3.746 -3.582 +569 1 26.798 4.052 -3.232 +570 1 27.066 4.766 -2.037 +571 1 21.17 1.379 -4.996 +572 1 21.438 2.658 -4.45 +573 1 22.778 3.047 -4.193 +574 1 23.046 4.052 -3.232 +575 1 24.386 4.326 -2.855 +576 1 24.654 4.93 -1.602 +577 1 25.994 5.053 -1.153 +578 1 26.262 5.178 0.233 +579 1 27.602 5.136 0.696 +580 1 27.87 4.766 2.037 +581 1 17.151 0.0 -5.183 +582 1 17.418 1.379 -4.996 +583 1 18.758 1.821 -4.853 +584 1 19.026 3.047 -4.193 +585 1 20.366 3.41 -3.903 +586 1 20.634 4.326 -2.855 +587 1 21.974 4.564 -2.456 +588 1 22.242 5.053 -1.153 +589 1 23.582 5.136 -0.696 +590 1 23.85 5.136 0.696 +591 1 25.19 5.053 1.153 +592 1 25.458 4.564 2.456 +593 1 26.798 4.326 2.855 +594 1 27.066 3.41 3.903 +595 1 17.954 3.746 -3.582 +596 1 18.222 4.564 -2.456 +597 1 19.562 4.766 -2.037 +598 1 19.83 5.136 -0.696 +599 1 21.17 5.178 -0.233 +600 1 21.438 5.053 1.153 +601 1 22.778 4.93 1.602 +602 1 23.046 4.326 2.855 +603 1 24.386 4.052 3.232 +604 1 24.654 3.047 4.193 +605 1 25.994 2.658 4.45 +606 1 26.262 1.379 4.996 +607 1 27.602 0.925 5.1 +608 1 27.87 -0.465 5.162 +609 1 17.151 4.93 -1.602 +610 1 17.418 5.178 -0.233 +611 1 18.758 5.178 0.233 +612 1 19.026 4.93 1.602 +613 1 20.366 4.766 2.037 +614 1 20.634 4.052 3.232 +615 1 21.974 3.746 3.582 +616 1 22.242 2.658 4.45 +617 1 23.582 2.249 4.67 +618 1 23.85 0.925 5.1 +619 1 25.19 0.465 5.162 +620 1 25.458 -0.925 5.1 +621 1 26.798 -1.379 4.996 +622 1 27.066 -2.658 4.45 +623 1 17.954 4.564 2.456 +624 1 18.222 3.746 3.582 +625 1 19.562 3.41 3.903 +626 1 19.83 2.249 4.67 +627 1 21.17 1.821 4.853 +628 1 21.438 0.465 5.162 +629 1 22.778 0.0 5.183 +630 1 23.046 -1.379 4.996 +631 1 24.386 -1.821 4.853 +632 1 24.654 -3.047 4.193 +633 1 25.994 -3.41 3.903 +634 1 26.262 -4.326 2.855 +635 1 27.602 -4.564 2.456 +636 1 27.87 -5.053 1.153 +637 1 17.151 3.047 4.193 +638 1 17.418 1.821 4.853 +639 1 18.758 1.379 4.996 +640 1 19.026 0.0 5.183 +641 1 20.366 -0.465 5.162 +642 1 20.634 -1.821 4.853 +643 1 21.974 -2.249 4.67 +644 1 22.242 -3.41 3.903 +645 1 23.582 -3.746 3.582 +646 1 23.85 -4.564 2.456 +647 1 25.19 -4.766 2.037 +648 1 25.458 -5.136 0.696 +649 1 26.798 -5.178 0.233 +650 1 27.066 -5.053 -1.153 +651 1 17.954 -0.925 5.1 +652 1 18.222 -2.249 4.67 +653 1 19.562 -2.658 4.45 +654 1 19.83 -3.746 3.582 +655 1 21.17 -4.052 3.232 +656 1 21.438 -4.766 2.037 +657 1 22.778 -4.93 1.602 +658 1 23.046 -5.178 0.233 +659 1 24.386 -5.178 -0.233 +660 1 24.654 -4.93 -1.602 +661 1 25.994 -4.766 -2.037 +662 1 26.262 -4.052 -3.232 +663 1 27.602 -3.746 -3.582 +664 1 27.87 -2.658 -4.45 +665 1 17.151 -3.047 4.193 +666 1 17.418 -4.052 3.232 +667 1 18.758 -4.326 2.855 +668 1 19.026 -4.93 1.602 +669 1 20.366 -5.053 1.153 +670 1 20.634 -5.178 -0.233 +671 1 21.974 -5.136 -0.696 +672 1 22.242 -4.766 -2.037 +673 1 23.582 -4.564 -2.456 +674 1 23.85 -3.746 -3.582 +675 1 25.19 -3.41 -3.903 +676 1 25.458 -2.249 -4.67 +677 1 26.798 -1.821 -4.853 +678 1 27.066 -0.465 -5.162 +679 1 17.954 -5.136 0.696 +680 1 18.222 -5.136 -0.696 +681 1 19.562 -5.053 -1.153 +682 1 19.83 -4.564 -2.456 +683 1 21.17 -4.326 -2.855 +684 1 21.438 -3.41 -3.903 +685 1 22.778 -3.047 -4.193 +686 1 23.046 -1.821 -4.853 +687 1 24.386 -1.379 -4.996 +688 1 24.654 0.0 -5.183 +689 1 17.151 -4.93 -1.602 +690 1 17.418 -4.326 -2.855 +691 1 18.758 -4.052 -3.232 +692 1 19.026 -3.047 -4.193 +693 1 20.366 -2.658 -4.45 +694 1 20.634 -1.379 -4.996 +695 1 21.974 -0.925 -5.1 +696 1 22.242 0.465 -5.162 +697 1 17.954 -2.249 -4.67 +698 1 18.222 -0.925 -5.1 +699 1 19.562 -0.465 -5.162 +700 1 19.83 0.925 -5.1 diff --git a/examples/GRAPHICS/data.cubes-and-pyramids b/examples/GRAPHICS/data.cubes-and-pyramids new file mode 100644 index 00000000000..282a730ed86 --- /dev/null +++ b/examples/GRAPHICS/data.cubes-and-pyramids @@ -0,0 +1,68 @@ +LAMMPS data file for polygons: cubes, moment of inertia I = m edge^2/ 6 +2 atoms +2 bodies +1 atom types +0 6 xlo xhi +0 6 ylo yhi +0 6 zlo zhi + +Atoms + +1 1 1 1 1.5 1.5 1.5 +2 1 1 1 4.0 4.0 4.0 + +Bodies + +1 3 79 +8 12 6 +0.667 0.667 0.667 0 0 0 +1 1 1 +1 -1 1 +-1 -1 1 +-1 1 1 +1 1 -1 +1 -1 -1 +-1 -1 -1 +-1 1 -1 +0 1 +1 2 +2 3 +3 0 +4 5 +5 6 +6 7 +7 4 +0 4 +1 5 +2 6 +3 7 +0 1 2 3 +4 5 6 7 +0 1 5 4 +1 2 6 5 +2 3 7 6 +3 0 4 7 +0.5 +2 3 58 +5 8 5 +0.72 0.72 0.8 0 0 0 +0 0 1.6 +1 1 -0.4 +1 -1 -0.4 +-1 -1 -0.4 +-1 1 -0.4 +0 1 +0 2 +0 3 +0 4 +1 2 +2 3 +3 4 +4 1 +1 2 3 4 +1 0 2 -1 +2 0 3 -1 +3 0 4 -1 +1 0 4 -1 +0.5 + diff --git a/examples/GRAPHICS/in.breakable b/examples/GRAPHICS/in.breakable new file mode 100644 index 00000000000..ab94c6a755f --- /dev/null +++ b/examples/GRAPHICS/in.breakable @@ -0,0 +1,107 @@ +# LAMMPS Input File +# Licensed under CC BY 4.0 +# A Set of Tutorials for the LAMMPS Simulation Package (LiveCoMS, 2025) +# By Simon Gravelle, Cecilia M. S. Alvares, Jacob R. Gissinger, and Axel Kohlmeyer +# Please cite doi.org/10.33011/livecoms.6.1.3037 +# Find more on GitHub: https://github.com/lammpstutorials + +units metal +atom_style atomic +boundary f f f + +read_data data.breakable +pair_style airebo 3.0 +pair_coeff * * CH.airebo C + +group carbon_atoms type 1 + +variable xmax equal bound(carbon_atoms,xmax)-0.5 +variable xmin equal bound(carbon_atoms,xmin)+0.5 +region rtop block ${xmax} INF INF INF INF INF +region rbot block INF ${xmin} INF INF INF INF +region rmid block ${xmin} ${xmax} INF INF INF INF + +group cnt_top region rtop +group cnt_bot region rbot +group cnt_mid region rmid + +variable xmax_del equal ${xmax}-2 +variable xmin_del equal ${xmin}+2 +region rdel block ${xmin_del} ${xmax_del} INF INF INF INF + +group rdel region rdel +delete_atoms random fraction 0.02 no rdel NULL 482793 + +reset_atoms id sort yes +velocity cnt_mid create 300 48455 mom yes rot yes + +fix mynve1 cnt_top nve +fix mynve2 cnt_bot nve +fix mynvt cnt_mid nvt temp 300 300 0.1 + +fix mysf1 cnt_bot setforce 0 0 0 +fix mysf2 cnt_top setforce 0 0 0 +velocity cnt_bot set 0 0 0 +velocity cnt_top set 0 0 0 + +variable Lcnt equal xcm(cnt_top,x)-xcm(cnt_bot,x) + +# compute and format simulation time +variable time equal step*dt +variable ps format time %5.2f + +# compute and format force on CNT edge atoms +variable Fcnt equal 0.5*(f_mysf1[1]-f_mysf2[1]) +variable force format Fcnt '% 7.3f' + +# velocity of CNT edge atoms +variable Vcnt equal vcm(cnt_top,x) +variable vel format Vcnt '% 4.2f' + +# compute bottom and tip position of left and right arrow +variable rbot equal -48.0 +variable rtop equal -46.0+0.1*v_Fcnt +variable ltop equal xcm(cnt_top,x)+8 +variable lbot equal xcm(cnt_top,x)+8-10*vcm(cnt_top,x) + +# count atoms with different numbers of bonds, i.e. neighbors closer than 1.8 Angstroms +compute nbond all coord/atom cutoff 1.8 +variable n1 atom c_nbond==1 +variable n2 atom c_nbond==2 +variable n3 atom c_nbond==3 +variable n4 atom c_nbond==4 +compute count all reduce sum v_n1 v_n2 v_n3 v_n4 + +# place arrows into visualizations. Use two fixes to give each a different color +fix arrows all graphics/objects 500 arrow 1 v_rbot 0.0 0.0 v_rtop 0.0 0.0 1.0 0.3 +fix vel all graphics/objects 500 arrow 1 v_lbot 0.0 0.0 v_ltop 0.0 0.0 1.0 0.3 + +# add text labels using different styles and references to variables and compute results +fix labels all graphics/labels 500 text "LAMMPS Tutorial 2 - Stretching CNT using AIREBO potential" 0.0 -7.7 5.3 size 28 & + framecolor black backcolor darkgray & + text "Simulation time: ${ps} ps" -4.0 7.5 5.0 size 28 & + fontcolor 220/200/0 backcolor darkgray transcolor darkgray framecolor darkgray & + text "Force: ${force} eV/Angstrom" -34 8.5 5.0 size 20 & + text "Velocity: ${vel} Angstrom/ps" -34 7.0 5.0 size 20 & + text "Atoms with bonds 1: $(c_count[1]) 2: $(c_count[2]) 3: $(c_count[3]) 4: $(c_count[4])" & + 32.5 7.5 5.0 size 24 fontcolor white backcolor silver + +dump viz all image 500 myimage.*.png c_nbond type size 1600 300 zoom 12.5 shiny 0.2 autobond 1.8 0.4 & + adiam 0.8 box no 0.01 view 0 90 fsaa yes ssao yes 1325123 0.6 axes yes 0.5 0.1 & + fix arrows const 0 0 fix labels const 0 0 fix vel const 0 0 +dump_modify viz pad 5 backcolor darkgray backcolor2 gray & + fcolor arrows firebrick fcolor vel midnightblue acolor 1 lightslategray & + amap 0 5 sa 1 5 midnightblue royalblue cadetblue lightslategray blue + +compute Tmid cnt_mid temp +thermo 100 +thermo_style custom step temp etotal v_Lcnt v_Fcnt v_lbot v_ltop +thermo_modify temp Tmid line yaml + +timestep 0.0005 +run 10000 + +velocity cnt_top set 0.75 0 0 +velocity cnt_bot set -0.75 0 0 + +run 40000 diff --git a/examples/GRAPHICS/in.cubes-and-pyramids b/examples/GRAPHICS/in.cubes-and-pyramids new file mode 100644 index 00000000000..f260fe1a852 --- /dev/null +++ b/examples/GRAPHICS/in.cubes-and-pyramids @@ -0,0 +1,52 @@ +# 3d rounded cubes and tetragonal pyramids + +variable r index 3 +variable steps index 1000 + +units lj +dimension 3 +boundary p p p + +atom_style body rounded/polyhedron 1 10 + +read_data data.cubes-and-pyramids extra/atom/types 1 + +set atom 2 type 2 + +replicate $r $r $r + +velocity all create 1.2 187287 dist gaussian mom yes rot yes + +variable cut_inner equal 0.5 +variable k_n equal 100 +variable k_na equal 1 +variable c_n equal 20 +variable c_t equal 5 +variable mu equal 0 +variable A_ua equal 1 + +pair_style body/rounded/polyhedron ${c_n} ${c_t} ${mu} ${A_ua} ${cut_inner} +pair_coeff * * ${k_n} ${k_na} + +comm_modify vel yes + +neighbor 0.5 bin +neigh_modify every 1 delay 0 check yes + +timestep 0.001 + +fix 1 all npt/body temp 1.2 1.2 0.1 iso 0.02 0.2 1.0 + +compute p2 all pressure 1_temp + +dump 2 all image 1000 image-bodies.*.png type type size 600 600 shiny 0.4 box yes 0.025 & + zoom 2.0 view 80 15 fsaa yes ssao yes 315123 0.8 body type 0.2 3 + +dump_modify 2 pad 6 backcolor darkgray backcolor2 gray boxcolor silver & + acolor 1 steelblue acolor 2 orange +thermo_style custom step ke pe etotal c_p2 c_1_temp + +thermo 1000 + +run ${steps} + diff --git a/python/setup.py b/python/setup.py index 72f8e883b7a..fac745134f0 100644 --- a/python/setup.py +++ b/python/setup.py @@ -18,6 +18,9 @@ def get_lammps_version(): start_pos = line.find('"')+1 end_pos = line.find('"', start_pos) t = time.strptime("".join(line[start_pos:end_pos].split()), "%d%b%Y") + line = f.readline() + if line.find("Development") >= 0 or line.find("Maintenance") >= 0: + return "{}.{}.{}".format(t.tm_year,t.tm_mon,t.tm_mday+1) return "{}.{}.{}".format(t.tm_year,t.tm_mon,t.tm_mday) class BinaryDistribution(Distribution): diff --git a/src/.gitignore b/src/.gitignore index 98fa5c4f777..df32c1da840 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -80,6 +80,27 @@ /fix_align_self.cpp /fix_align_self.h +/dump_image.cpp +/dump_image.h +/dump_movie.cpp +/dump_movie.h +/fix_graphics_arrows.cpp +/fix_graphics_arrows.h +/fix_graphics_isosurface.cpp +/fix_graphics_isosurface.h +/fix_graphics_labels.cpp +/fix_graphics_labels.h +/fix_graphics_objects.cpp +/fix_graphics_objects.h +/fix_graphics_periodic.cpp +/fix_graphics_periodic.h +/image.cpp +/image.h +/image_objects.cpp +/image_objects.h +/scalable_font.cpp +/scalable_font.h + /kim_*.cpp /kim_*.h /pair_kim.cpp @@ -1849,4 +1870,3 @@ /pair_vashishta*.h /pair_lj_pirani.cpp /pair_lj_pirani.h - diff --git a/src/BODY/body_nparticle.cpp b/src/BODY/body_nparticle.cpp index 0e1a8fc30dc..d30c4eaad30 100644 --- a/src/BODY/body_nparticle.cpp +++ b/src/BODY/body_nparticle.cpp @@ -16,8 +16,8 @@ #include "atom.h" #include "atom_vec_body.h" -#include "dump_image.h" #include "error.h" +#include "graphics.h" #include "math_extra.h" #include "math_eigen.h" #include "memory.h" @@ -355,7 +355,7 @@ int BodyNparticle::image(int ibonus, double flag1, double /*flag2*/, int n = bonus->ivalue[0]; for (int i = 0; i < n; i++) { - imflag[i] = DumpImage::SPHERE; + imflag[i] = Graphics::SPHERE; MathExtra::quat_to_mat(bonus->quat,p); MathExtra::matvec(p,&bonus->dvalue[3*i],imdata[i]); diff --git a/src/BODY/body_rounded_polygon.cpp b/src/BODY/body_rounded_polygon.cpp index 3fc366440fd..7c88c842727 100644 --- a/src/BODY/body_rounded_polygon.cpp +++ b/src/BODY/body_rounded_polygon.cpp @@ -21,8 +21,8 @@ #include "atom.h" #include "atom_vec_body.h" #include "domain.h" -#include "dump_image.h" #include "error.h" +#include "graphics.h" #include "math_extra.h" #include "math_eigen.h" #include "memory.h" @@ -43,15 +43,13 @@ BodyRoundedPolygon::BodyRoundedPolygon(LAMMPS *lmp, int narg, char **arg) : if (narg != 3) error->all(FLERR,"Invalid body rounded/polygon command"); if (domain->dimension != 2) - error->all(FLERR,"Atom_style body rounded/polygon " - "can only be used in 2d simulations"); + error->all(FLERR,"Atom_style body rounded/polygon can only be used in 2d simulations"); // nmin and nmax are minimum and maximum number of vertices int nmin = utils::inumeric(FLERR,arg[1],false,lmp); int nmax = utils::inumeric(FLERR,arg[2],false,lmp); - if (nmin <= 0 || nmin > nmax) - error->all(FLERR,"Invalid body rounded/polygon command"); + if (nmin <= 0 || nmin > nmax) error->all(FLERR,"Invalid body rounded/polygon command"); size_forward = 0; @@ -68,8 +66,8 @@ BodyRoundedPolygon::BodyRoundedPolygon(LAMMPS *lmp, int narg, char **arg) : dcp = new MyPoolChunk(3*nmin+2*nmin+1+1,3*nmax+2*nmax+1+1); maxexchange = 1 + 3*nmax+2*nmax+1+1; // icp max + dcp max - memory->create(imflag,nmax,"body/rounded/polygon:imflag"); - memory->create(imdata,nmax,7,"body/nparticle:imdata"); + memory->create(imflag,2*nmax,"body/rounded/polygon:imflag"); + memory->create(imdata,2*nmax,9,"body/rounded/polygon:imdata"); } /* ---------------------------------------------------------------------- */ @@ -166,20 +164,16 @@ void BodyRoundedPolygon::data_body(int ibonus, int ninteger, int ndouble, // set ninteger, ndouble in bonus and allocate 2 vectors of ints, doubles if (ninteger != 1) - error->one(FLERR,"Incorrect # of integer values in " - "Bodies section of data file"); + error->one(FLERR,"Incorrect # of integer values in Bodies section of data file"); int nsub = ifile[0]; - if (nsub < 1) - error->one(FLERR,"Incorrect integer value in " - "Bodies section of data file"); + if (nsub < 1) error->one(FLERR,"Incorrect integer value in Bodies section of data file"); // nentries = number of double entries to be read from Body section: // 6 for inertia + 3*nsub for vertex coords + 1 for rounded radius int nentries = 6 + 3*nsub + 1; if (ndouble != nentries) - error->one(FLERR,"Incorrect # of floating-point values in " - "Bodies section of data file"); + error->one(FLERR,"Incorrect # of floating-point values in Bodies section of data file"); bonus->ninteger = 1; bonus->ivalue = icp->get(bonus->iindex); @@ -201,8 +195,7 @@ void BodyRoundedPolygon::data_body(int ibonus, int ninteger, int ndouble, double *inertia = bonus->inertia; double evectors[3][3]; int ierror = MathEigen::jacobi3(tensor,inertia,evectors); - if (ierror) error->one(FLERR, - "Insufficient Jacobi rotations for body nparticle"); + if (ierror) error->one(FLERR,"Insufficient Jacobi rotations for body nparticle"); // if any principal moment < scaled EPSILON, set to 0.0 @@ -434,11 +427,9 @@ double BodyRoundedPolygon::radius_body(int /*ninteger*/, int ndouble, { int nsub = ifile[0]; if (nsub < 1) - error->one(FLERR,"Incorrect integer value in " - "Bodies section of data file"); + error->one(FLERR,"Incorrect integer value in Bodies section of data file"); if (ndouble != 6 + 3*nsub + 1) - error->one(FLERR,"Incorrect # of floating-point values in " - "Bodies section of data file"); + error->one(FLERR,"Incorrect # of floating-point values in Bodies section of data file"); // sub-particle coords are relative to body center at (0,0,0) // offset = 6 for sub-particle coords @@ -498,61 +489,100 @@ void BodyRoundedPolygon::output(int ibonus, int m, double *values) /* ---------------------------------------------------------------------- */ -int BodyRoundedPolygon::image(int ibonus, double flag1, double /*flag2*/, +int BodyRoundedPolygon::image(int ibonus, double flag1, double flag2, int *&ivec, double **&darray) { - int j; double p[3][3]; - double *x, rrad; + int nelements = 0; AtomVecBody::Bonus *bonus = &avec->bonus[ibonus]; - int n = bonus->ivalue[0]; - - if (n == 1) { - for (int i = 0; i < n; i++) { - imflag[i] = DumpImage::SPHERE; - MathExtra::quat_to_mat(bonus->quat,p); - MathExtra::matvec(p,&bonus->dvalue[3*i],imdata[i]); + const double *const x = atom->x[bonus->ilocal]; + const double diam = (flag1 <= 0.0) ? 2.0 * rounded_radius(bonus) : flag1; - rrad = enclosing_radius(bonus); - x = atom->x[bonus->ilocal]; - imdata[i][0] += x[0]; - imdata[i][1] += x[1]; - imdata[i][2] += x[2]; - if (flag1 <= 0) imdata[i][3] = 2*rrad; - else imdata[i][3] = flag1; - } + MathExtra::quat_to_mat(bonus->quat,p); // get rotation matrix for body frame to box frame + int n = bonus->ivalue[0]; + if (n == 1) { // special case: one vertex -> one sphere + imflag[0] = Graphics::SPHERE; + // transform body frame position to box frame + MathExtra::matvec(p,&bonus->dvalue[0],imdata[0]); + // translate and set diameter + imdata[0][0] += x[0]; + imdata[0][1] += x[1]; + imdata[0][2] = 0.0; + imdata[0][3] = diam; + + nelements = 1; } else { - // first end pt of each line + // select whether edges or faces or both should be drawn + bool edgeflag = true; + bool triflag = true; + int flag = static_cast(flag2); + if (flag == 2) triflag = false; + if (flag == 1) edgeflag = false; + // copy first coordinate of connected lines for (int i = 0; i < n; i++) { - imflag[i] = DumpImage::LINE; - MathExtra::quat_to_mat(bonus->quat,p); MathExtra::matvec(p,&bonus->dvalue[3*i],imdata[i]); - - rrad = rounded_radius(bonus); - x = atom->x[bonus->ilocal]; imdata[i][0] += x[0]; imdata[i][1] += x[1]; - imdata[i][2] += x[2]; - if (flag1 <= 0) imdata[i][6] = 2*rrad; - else imdata[i][6] = flag1; + imdata[i][2] = 0.0; } - // second end pt of each line + // add lines to next coordinate and wrap to first + if (n == 2) n = 1; // special case: two vertices -> one rod only + if (edgeflag || n == 1) { // always draw line for rod + for (int i = 0; i < n; i++) { + // register element type and copy second coordinate + imflag[nelements] = Graphics::LINE; + int next = nelements + 1; + if (next == n) next = 0; + imdata[nelements][3] = imdata[next][0]; + imdata[nelements][4] = imdata[next][1]; + imdata[nelements][5] = 0.0; + imdata[nelements][6] = diam; + ++nelements; + } + } - for (int i = 0; i < n; i++) { - j = i+1; - if (j == n) j = 0; - imdata[i][3] = imdata[j][0]; - imdata[i][4] = imdata[j][1]; - imdata[i][5] = imdata[j][2]; + if (triflag && (n > 1)) { + double center[3] = {0.0, 0.0, 0.0}; + // copy first outer coordinate + for (int i = 0; i < n; i++) { + MathExtra::matvec(p,&bonus->dvalue[3*i],imdata[nelements+i]); + imdata[nelements+i][0] += x[0]; + imdata[nelements+i][1] += x[1]; + imdata[nelements+i][2] = edgeflag ? 0.5*diam : 0.0; + center[0] += imdata[nelements+i][0]; + center[1] += imdata[nelements+i][1]; + center[2] += imdata[nelements+i][2]; + } + double invn = 1.0/n; + center[0] *= invn; + center[1] *= invn; + center[2] *= invn; + + // construct triangles + const int nmax = nelements + n; + for (int i = 0; i < n; i++) { + // register element type and copy second coordinate with wrap and center + imflag[nelements] = Graphics::TRI; + int next = nelements + 1; + if (next == nmax) next = nmax - n; + imdata[nelements][3] = center[0]; + imdata[nelements][4] = center[1]; + imdata[nelements][5] = center[2]; + imdata[nelements][6] = imdata[next][0]; + imdata[nelements][7] = imdata[next][1]; + imdata[nelements][8] = edgeflag ? 0.5*diam : 0.0; + + ++nelements; + } } } ivec = imflag; darray = imdata; - return n; + return nelements; } diff --git a/src/BODY/body_rounded_polyhedron.cpp b/src/BODY/body_rounded_polyhedron.cpp index 30b757eb2bc..9e256735586 100644 --- a/src/BODY/body_rounded_polyhedron.cpp +++ b/src/BODY/body_rounded_polyhedron.cpp @@ -20,8 +20,8 @@ #include "atom.h" #include "atom_vec_body.h" -#include "dump_image.h" #include "error.h" +#include "graphics.h" #include "math_extra.h" #include "math_eigen.h" #include "memory.h" @@ -46,8 +46,7 @@ BodyRoundedPolyhedron::BodyRoundedPolyhedron(LAMMPS *lmp, int narg, char **arg) int nmin = utils::inumeric(FLERR,arg[1],false,lmp); int nmax = utils::inumeric(FLERR,arg[2],false,lmp); - if (nmin <= 0 || nmin > nmax) - error->all(FLERR,"Invalid body rounded/polyhedron command"); + if (nmin <= 0 || nmin > nmax) error->all(FLERR,"Invalid body rounded/polyhedron command"); size_forward = 0; @@ -64,8 +63,8 @@ BodyRoundedPolyhedron::BodyRoundedPolyhedron(LAMMPS *lmp, int narg, char **arg) 3*nmax+2*nmax+MAX_FACE_SIZE*nmax+1+1); maxexchange = 3 + 3*nmax+2*nmax+MAX_FACE_SIZE*nmax+1+1; // icp max + dcp max - memory->create(imflag,2*nmax,"body/rounded/polyhedron:imflag"); - memory->create(imdata,2*nmax,7,"body/polyhedron:imdata"); + memory->create(imflag,3*nmax,"body/rounded/polyhedron:imflag"); + memory->create(imdata,3*nmax,9,"body/rounded/polyhedron:imdata"); } /* ---------------------------------------------------------------------- */ @@ -197,14 +196,11 @@ void BodyRoundedPolyhedron::data_body(int ibonus, int ninteger, int ndouble, // set ninteger, ndouble in bonus and allocate 2 vectors of ints, doubles if (ninteger != 3) - error->one(FLERR,"Incorrect # of integer values in " - "Bodies section of data file"); + error->one(FLERR,"Incorrect # of integer values in Bodies section of data file"); int nsub = ifile[0]; int ned = ifile[1]; int nfac = ifile[2]; - if (nsub < 1) - error->one(FLERR,"Incorrect integer value in " - "Bodies section of data file"); + if (nsub < 1) error->one(FLERR,"Incorrect integer value in Bodies section of data file"); // nentries = number of double entries to be read from Body section: // nsub == 1,2: @@ -221,8 +217,7 @@ void BodyRoundedPolyhedron::data_body(int ibonus, int ninteger, int ndouble, } if (ndouble != nentries) - error->one(FLERR,"Incorrect # of floating-point values in " - "Bodies section of data file"); + error->one(FLERR,"Incorrect # of floating-point values in Bodies section of data file"); bonus->ninteger = 3; bonus->ivalue = icp->get(bonus->iindex); @@ -246,8 +241,7 @@ void BodyRoundedPolyhedron::data_body(int ibonus, int ninteger, int ndouble, double *inertia = bonus->inertia; double evectors[3][3]; int ierror = MathEigen::jacobi3(tensor,inertia,evectors); - if (ierror) error->one(FLERR, - "Insufficient Jacobi rotations for body nparticle"); + if (ierror) error->one(FLERR,"Insufficient Jacobi rotations for body nparticle"); // if any principal moment < scaled EPSILON, set to 0.0 @@ -535,11 +529,9 @@ double BodyRoundedPolyhedron::radius_body(int /*ninteger*/, int ndouble, else nentries = 6 + 3*nsub + 2*nedges + MAX_FACE_SIZE*nfac + 1; if (nsub < 1) - error->one(FLERR,"Incorrect integer value in " - "Bodies section of data file"); + error->one(FLERR,"Incorrect integer value in Bodies section of data file"); if (ndouble != nentries) - error->one(FLERR,"Incorrect # of floating-point values in " - "Bodies section of data file"); + error->one(FLERR,"Incorrect # of floating-point values in Bodies section of data file"); // sub-particle coords are relative to body center at (0,0,0) // offset = 6 for sub-particle coords @@ -601,64 +593,189 @@ void BodyRoundedPolyhedron::output(int ibonus, int m, double *values) /* ---------------------------------------------------------------------- */ -int BodyRoundedPolyhedron::image(int ibonus, double flag1, double /*flag2*/, - int *&ivec, double **&darray) +int BodyRoundedPolyhedron::image(int ibonus, double flag1, double flag2, + int *&ivec, double **&darray) { - int nelements; double p[3][3]; - double *x, rrad; - AtomVecBody::Bonus *bonus = &avec->bonus[ibonus]; - int nvertices = bonus->ivalue[0]; + int nelements = 0; + AtomVecBody::Bonus *const bonus = &avec->bonus[ibonus]; + const double *const x = atom->x[bonus->ilocal]; + const double diam = (flag1 <= 0.0) ? 2.0 * rounded_radius(bonus) : flag1; - if (nvertices == 1) { // spheres + MathExtra::quat_to_mat(bonus->quat,p); // get rotation matrix for body frame to box frame - for (int i = 0; i < nvertices; i++) { - imflag[i] = DumpImage::SPHERE; - MathExtra::quat_to_mat(bonus->quat,p); - MathExtra::matvec(p,&bonus->dvalue[3*i],imdata[i]); + int nvertices = bonus->ivalue[0]; + if (nvertices == 1) { // special case: just one vertex -> one sphere + imflag[0] = Graphics::SPHERE; + // transform body frame position to box frame + MathExtra::matvec(p,&bonus->dvalue[0],imdata[0]); + // translate and set diameter + imdata[0][0] += x[0]; + imdata[0][1] += x[1]; + imdata[0][2] += x[2]; + imdata[0][3] = diam; + + nelements = 1; + } else { - rrad = enclosing_radius(bonus); - x = atom->x[bonus->ilocal]; - imdata[i][0] += x[0]; - imdata[i][1] += x[1]; - imdata[i][2] += x[2]; - if (flag1 <= 0) imdata[i][3] = 2*rrad; - else imdata[i][3] = flag1; + // select whether edges or faces or both should be drawn + bool edgeflag = true; + bool triflag = true; + int flag = static_cast(flag2); + if (flag == 2) triflag = false; + if (flag == 1) edgeflag = false; + + int nedges = bonus->ivalue[1]; + if (nvertices == 2) nedges = 1; // special case: just two vertices -> one rod + double *edge_ends = &bonus->dvalue[3*nvertices]; // skip over vertex positions in body data + if (edgeflag || (nedges == 1)) { // always draw edge for rod + for (int i = 0; i < nedges; i++) { + imflag[nelements] = Graphics::LINE; + + int pt1 = static_cast(edge_ends[2*i]); + int pt2 = static_cast(edge_ends[2*i+1]); + + // transform body frame positions to box frame + MathExtra::matvec(p,&bonus->dvalue[3*pt1],imdata[nelements]); + MathExtra::matvec(p,&bonus->dvalue[3*pt2],&imdata[nelements][3]); + + // translate and set diameter + imdata[nelements][0] += x[0]; + imdata[nelements][1] += x[1]; + imdata[nelements][2] += x[2]; + imdata[nelements][3] += x[0]; + imdata[nelements][4] += x[1]; + imdata[nelements][5] += x[2]; + imdata[nelements][6] = diam; + + ++nelements; + } } - nelements = nvertices; - } else { - //int nfaces = bonus->ivalue[2]; - int nedges = bonus->ivalue[1]; //nvertices + nfaces - 2; - if (nvertices == 2) nedges = 1; // special case: rods - double* edge_ends = &bonus->dvalue[3*nvertices]; - int pt1, pt2; - - for (int i = 0; i < nedges; i++) { - imflag[i] = DumpImage::LINE; - - pt1 = static_cast(edge_ends[2*i]); - pt2 = static_cast(edge_ends[2*i+1]); - - MathExtra::quat_to_mat(bonus->quat,p); - MathExtra::matvec(p,&bonus->dvalue[3*pt1],imdata[i]); - MathExtra::matvec(p,&bonus->dvalue[3*pt2],&imdata[i][3]); - - rrad = rounded_radius(bonus); - x = atom->x[bonus->ilocal]; - imdata[i][0] += x[0]; - imdata[i][1] += x[1]; - imdata[i][2] += x[2]; - imdata[i][3] += x[0]; - imdata[i][4] += x[1]; - imdata[i][5] += x[2]; - - if (flag1 <= 0) imdata[i][6] = 2*rrad; - else imdata[i][6] = flag1; + if (triflag) { + int nfaces = bonus->ivalue[2]; + // skip over vertex positions and edge indices in body data + edge_ends = &bonus->dvalue[3*nvertices+2*nedges]; + for (int i = 0; i < nfaces; i++) { + int pt1 = static_cast(edge_ends[4*i]); + int pt2 = static_cast(edge_ends[4*i+1]); + int pt3 = static_cast(edge_ends[4*i+2]); + int pt4 = static_cast(edge_ends[4*i+3]); + + // quadrilateral face requires two triangles. triangle has fourth vertex index set to -1 + if (pt4 >= 0) { + // first triangle + imflag[nelements] = Graphics::TRI; + MathExtra::matvec(p,&bonus->dvalue[3*pt1],imdata[nelements]); + MathExtra::matvec(p,&bonus->dvalue[3*pt2],&imdata[nelements][3]); + MathExtra::matvec(p,&bonus->dvalue[3*pt3],&imdata[nelements][6]); + imdata[nelements][0] += x[0]; + imdata[nelements][1] += x[1]; + imdata[nelements][2] += x[2]; + imdata[nelements][3] += x[0]; + imdata[nelements][4] += x[1]; + imdata[nelements][5] += x[2]; + imdata[nelements][6] += x[0]; + imdata[nelements][7] += x[1]; + imdata[nelements][8] += x[2]; + ++nelements; + + // second triangle + imflag[nelements] = Graphics::TRI; + MathExtra::matvec(p,&bonus->dvalue[3*pt3],imdata[nelements]); + MathExtra::matvec(p,&bonus->dvalue[3*pt4],&imdata[nelements][3]); + MathExtra::matvec(p,&bonus->dvalue[3*pt1],&imdata[nelements][6]); + imdata[nelements][0] += x[0]; + imdata[nelements][1] += x[1]; + imdata[nelements][2] += x[2]; + imdata[nelements][3] += x[0]; + imdata[nelements][4] += x[1]; + imdata[nelements][5] += x[2]; + imdata[nelements][6] += x[0]; + imdata[nelements][7] += x[1]; + imdata[nelements][8] += x[2]; + + // shift triangles toward the outside of the body by half diameter when also drawing edges + if (edgeflag) { + double vec1[3]; + // get center of face + double vec2[3] = {imdata[nelements][0],imdata[nelements][1],imdata[nelements][2]}; + vec2[0] += imdata[nelements][3] + imdata[nelements][6] + imdata[nelements-1][3]; + vec2[1] += imdata[nelements][4] + imdata[nelements][7] + imdata[nelements-1][4]; + vec2[2] += imdata[nelements][5] + imdata[nelements][8] + imdata[nelements-1][5]; + vec2[0] *= 0.25; + vec2[1] *= 0.25; + vec2[2] *= 0.25; + // get direction from center of body to face and scale to half diameter length + MathExtra::sub3(vec2,x,vec1); + MathExtra::snormalize3(0.5*diam,vec1,vec2); + // add shift to triangle corners + imdata[nelements][0] += vec2[0]; + imdata[nelements][1] += vec2[1]; + imdata[nelements][2] += vec2[2]; + imdata[nelements][3] += vec2[0]; + imdata[nelements][4] += vec2[1]; + imdata[nelements][5] += vec2[2]; + imdata[nelements][6] += vec2[0]; + imdata[nelements][7] += vec2[1]; + imdata[nelements][8] += vec2[2]; + imdata[nelements-1][0] += vec2[0]; + imdata[nelements-1][1] += vec2[1]; + imdata[nelements-1][2] += vec2[2]; + imdata[nelements-1][3] += vec2[0]; + imdata[nelements-1][4] += vec2[1]; + imdata[nelements-1][5] += vec2[2]; + imdata[nelements-1][6] += vec2[0]; + imdata[nelements-1][7] += vec2[1]; + imdata[nelements-1][8] += vec2[2]; + } + + ++nelements; + } else { + imflag[nelements] = Graphics::TRI; + MathExtra::matvec(p,&bonus->dvalue[3*pt1],imdata[nelements]); + MathExtra::matvec(p,&bonus->dvalue[3*pt2],&imdata[nelements][3]); + MathExtra::matvec(p,&bonus->dvalue[3*pt3],&imdata[nelements][6]); + imdata[nelements][0] += x[0]; + imdata[nelements][1] += x[1]; + imdata[nelements][2] += x[2]; + imdata[nelements][3] += x[0]; + imdata[nelements][4] += x[1]; + imdata[nelements][5] += x[2]; + imdata[nelements][6] += x[0]; + imdata[nelements][7] += x[1]; + imdata[nelements][8] += x[2]; + + // shift triangle toward the outside of the body by half diameter when also drawing edges + if (edgeflag) { + double vec1[3]; + // get center of face + double vec2[3] = {imdata[nelements][0],imdata[nelements][1],imdata[nelements][2]}; + vec2[0] += imdata[nelements][3] + imdata[nelements][6]; + vec2[1] += imdata[nelements][4] + imdata[nelements][7]; + vec2[2] += imdata[nelements][5] + imdata[nelements][8]; + vec2[0] *= 1.0/3.0; + vec2[1] *= 1.0/3.0; + vec2[2] *= 1.0/3.0; + // get direction from center of body to face and scale to half diameter length + MathExtra::sub3(vec2,x,vec1); + MathExtra::snormalize3(0.5*diam,vec1,vec2); + // add shift to triangle corners + imdata[nelements][0] += vec2[0]; + imdata[nelements][1] += vec2[1]; + imdata[nelements][2] += vec2[2]; + imdata[nelements][3] += vec2[0]; + imdata[nelements][4] += vec2[1]; + imdata[nelements][5] += vec2[2]; + imdata[nelements][6] += vec2[0]; + imdata[nelements][7] += vec2[1]; + imdata[nelements][8] += vec2[2]; + } + ++nelements; + } + } } - - nelements = nedges; } ivec = imflag; diff --git a/src/BODY/fix_wall_body_polygon.cpp b/src/BODY/fix_wall_body_polygon.cpp index 051d316a28b..8847573ec1a 100644 --- a/src/BODY/fix_wall_body_polygon.cpp +++ b/src/BODY/fix_wall_body_polygon.cpp @@ -23,7 +23,9 @@ #include "body_rounded_polygon.h" #include "domain.h" #include "error.h" +#include "fix_wall.h" #include "force.h" +#include "graphics.h" #include "math_const.h" #include "math_extra.h" #include "memory.h" @@ -36,13 +38,11 @@ using namespace LAMMPS_NS; using namespace FixConst; using namespace MathConst; -enum{XPLANE=0,YPLANE=1,ZCYLINDER}; // XYZ PLANE need to be 0,1,2 -enum{HOOKE,HOOKE_HISTORY}; +enum{XPLANE=0,YPLANE=1,ZPLANE=2}; // XYZ PLANE need to be 0,1 enum {INVALID=0,NONE=1,VERTEX=2}; enum {FAR=0,XLO,XHI,YLO,YHI}; -//#define _POLYGON_DEBUG static constexpr int DELTA = 10000; static constexpr double EPSILON = 1.0e-2; // dimensionless threshold (dot products, end point checks, contact checks) static constexpr double BIG = 1.0e20; @@ -52,13 +52,12 @@ static constexpr int EFF_CONTACTS = 2; // effective contacts for 2D models /* ---------------------------------------------------------------------- */ FixWallBodyPolygon::FixWallBodyPolygon(LAMMPS *lmp, int narg, char **arg) : - Fix(lmp, narg, arg) + Fix(lmp, narg, arg), imgobjs(nullptr), imgparms(nullptr) { - if (narg < 7) error->all(FLERR,"Illegal fix wall/body/polygon command"); + if (narg < 9) utils::missing_cmd_args(FLERR,"fix wall/body/polygon", error); if (!atom->body_flag) - error->all(FLERR,"Fix wall/body/polygon requires " - "atom style body/rounded/polygon"); + error->all(FLERR,"Fix wall/body/polygon requires atom style body/rounded/polygon"); restart_peratom = 1; create_attribute = 1; @@ -67,69 +66,75 @@ FixWallBodyPolygon::FixWallBodyPolygon(LAMMPS *lmp, int narg, char **arg) : // wall/particle coefficients kn = utils::numeric(FLERR,arg[3],false,lmp); - + if (kn < 0.0) error->all(FLERR,3,"Illegal fix wall/body/polygon argument {}", kn); c_n = utils::numeric(FLERR,arg[4],false,lmp); + if (c_n < 0.0) + error->all(FLERR,4,"Illegal fix wall/body/polygon argument {}", c_n); if (strcmp(arg[5],"NULL") == 0) c_t = 0.5 * c_n; else c_t = utils::numeric(FLERR,arg[5],false,lmp); - - if (kn < 0.0 || c_n < 0.0 || c_t < 0.0) - error->all(FLERR,"Illegal fix wall/body/polygon command"); + if (c_t < 0.0) + error->all(FLERR,5,"Illegal fix wall/body/polygon argument {}", c_t); // wallstyle args - int iarg = 6; - if (strcmp(arg[iarg],"xplane") == 0) { - if (narg < iarg+3) error->all(FLERR,"Illegal fix wall/body/polygon command"); + numwalls = 0; + if (strcmp(arg[6],"xplane") == 0) { wallstyle = XPLANE; - if (strcmp(arg[iarg+1],"NULL") == 0) lo = -BIG; - else lo = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (strcmp(arg[iarg+2],"NULL") == 0) hi = BIG; - else hi = utils::numeric(FLERR,arg[iarg+2],false,lmp); - iarg += 3; - } else if (strcmp(arg[iarg],"yplane") == 0) { - if (narg < iarg+3) error->all(FLERR,"Illegal fix wall/body/polygon command"); + if (strcmp(arg[7],"NULL") == 0) { + lo = -BIG; + } else { + lo = utils::numeric(FLERR,arg[7],false,lmp); + ++numwalls; + } + if (strcmp(arg[8],"NULL") == 0) { + hi = BIG; + } else { + hi = utils::numeric(FLERR,arg[8],false,lmp); + ++numwalls; + } + + } else if (strcmp(arg[6],"yplane") == 0) { wallstyle = YPLANE; - if (strcmp(arg[iarg+1],"NULL") == 0) lo = -BIG; - else lo = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (strcmp(arg[iarg+2],"NULL") == 0) hi = BIG; - else hi = utils::numeric(FLERR,arg[iarg+2],false,lmp); - iarg += 3; - } else if (strcmp(arg[iarg],"zcylinder") == 0) { - if (narg < iarg+2) error->all(FLERR,"Illegal fix wall/body/polygon command"); - wallstyle = ZCYLINDER; - lo = hi = 0.0; - cylradius = utils::numeric(FLERR,arg[iarg+1],false,lmp); - iarg += 2; - } else error->all(FLERR,"Unknown wall style {}",arg[iarg]); + if (strcmp(arg[7],"NULL") == 0) { + lo = -BIG; + } else { + lo = utils::numeric(FLERR,arg[7],false,lmp); + ++numwalls; + } + if (strcmp(arg[8],"NULL") == 0) { + hi = BIG; + } else { + hi = utils::numeric(FLERR,arg[8],false,lmp); + ++numwalls; + } + } else error->all(FLERR,"Unknown wall style {}",arg[6]); + + if (wallstyle == XPLANE && domain->xperiodic) + error->all(FLERR, 6, "Cannot use wall in periodic dimension"); + if (wallstyle == YPLANE && domain->yperiodic) + error->all(FLERR, 6, "Cannot use wall in periodic dimension"); // check for trailing keyword/values wiggle = 0; - + int iarg = 9; while (iarg < narg) { if (strcmp(arg[iarg],"wiggle") == 0) { - if (iarg+4 > narg) error->all(FLERR,"Illegal fix wall/body/polygon command"); - if (strcmp(arg[iarg+1],"x") == 0) axis = 0; - else if (strcmp(arg[iarg+1],"y") == 0) axis = 1; - else if (strcmp(arg[iarg+1],"z") == 0) axis = 2; - else error->all(FLERR,"Illegal fix wall/body/polygon command"); + if (iarg+4 > narg) + utils::missing_cmd_args(FLERR,"fix wall/body/polygon wiggle", error); + + if (strcmp(arg[iarg+1],"x") == 0) axis = XPLANE; + else if (strcmp(arg[iarg+1],"y") == 0) axis = YPLANE; + else if (strcmp(arg[iarg+1],"z") == 0) axis = ZPLANE; + else error->all(FLERR, iarg+1, + "Illegal fix wall/body/polygon wiggle direction {}", arg[iarg+1]); amplitude = utils::numeric(FLERR,arg[iarg+2],false,lmp); period = utils::numeric(FLERR,arg[iarg+3],false,lmp); wiggle = 1; iarg += 4; - } else error->all(FLERR,"Illegal fix wall/body/polygon command"); + } else error->all(FLERR, iarg, "Unknown fix wall/body/polygon keyword {}", arg[iarg]); } - if (wallstyle == XPLANE && domain->xperiodic) - error->all(FLERR,"Cannot use wall in periodic dimension"); - if (wallstyle == YPLANE && domain->yperiodic) - error->all(FLERR,"Cannot use wall in periodic dimension"); - if (wallstyle == ZCYLINDER && (domain->xperiodic || domain->yperiodic)) - error->all(FLERR,"Cannot use wall in periodic dimension"); - - if (wiggle && wallstyle == ZCYLINDER && axis != 2) - error->all(FLERR,"Invalid wiggle direction for fix wall/body/polygon"); - // setup oscillations if (wiggle) omega = 2.0*MY_PI / period; @@ -146,6 +151,19 @@ FixWallBodyPolygon::FixWallBodyPolygon(LAMMPS *lmp, int narg, char **arg) : enclosing_radius = nullptr; rounded_radius = nullptr; + + // for rendering walls with dump image. + + if (numwalls > 0) { + // this body style only supports 2d + // one cylinder object per wall to draw + memory->create(imgobjs, numwalls, "fix_wall:imgobjs"); + memory->create(imgparms, numwalls, 8, "fix_wall:imgparms"); + for (int m = 0; m < numwalls; ++m) { + imgobjs[m] = Graphics::CYLINDER; + imgparms[m][0] = 1; // use color of first atom type by default + } + } } /* ---------------------------------------------------------------------- */ @@ -162,6 +180,9 @@ FixWallBodyPolygon::~FixWallBodyPolygon() memory->destroy(enclosing_radius); memory->destroy(rounded_radius); + + memory->destroy(imgobjs); + memory->destroy(imgparms); } /* ---------------------------------------------------------------------- */ @@ -183,15 +204,11 @@ void FixWallBodyPolygon::init() if (!avec) error->all(FLERR,"Pair body/rounded/polygon requires atom style body"); if (strcmp(avec->bptr->style,"rounded/polygon") != 0) - error->all(FLERR,"Pair body/rounded/polygon requires " - "body style rounded/polygon"); + error->all(FLERR,"Pair body/rounded/polygon requires body style rounded/polygon"); bptr = dynamic_cast(avec->bptr); - // set pairstyle from body/polygonular pair style - - if (force->pair_match("body/rounded/polygon",1)) - pairstyle = HOOKE; - else error->all(FLERR,"Fix wall/body/polygon is incompatible with Pair style"); + if (!force->pair_match("body/rounded/polygon",1)) + error->all(FLERR,"Fix wall/body/polygon is incompatible with Pair style"); } /* ---------------------------------------------------------------------- */ @@ -206,7 +223,7 @@ void FixWallBodyPolygon::setup(int vflag) void FixWallBodyPolygon::post_force(int /*vflag*/) { - double vwall[3],dx,dy,dz,del1,del2,delxy,delr,rsq,eradi,wall_pos; + double vwall[3],dx,dy,dz,del1,del2,rsq,wall_pos; int i,ni,npi,ifirst,nei,iefirst,side; double facc[3]; @@ -228,7 +245,6 @@ void FixWallBodyPolygon::post_force(int /*vflag*/) // loop over all my atoms // rsq = distance from wall // dx,dy,dz = signed distance from wall - // for rotating cylinder, reset vwall based on particle position // skip atom if not close enough to wall // if wall was set to a null pointer, it's skipped since lo/hi are infinity // compute force and torque on atom if close enough to wall @@ -297,14 +313,6 @@ void FixWallBodyPolygon::post_force(int /*vflag*/) wall_pos = whi; side = YHI; } - } else if (wallstyle == ZCYLINDER) { - delxy = sqrt(x[i][0]*x[i][0] + x[i][1]*x[i][1]); - delr = cylradius - delxy; - if (delr > eradi) dz = cylradius; - else { - dx = -delr/delxy * x[i][0]; - dy = -delr/delxy * x[i][1]; - } } rsq = dx*dx + dy*dy + dz*dz; @@ -315,7 +323,6 @@ void FixWallBodyPolygon::post_force(int /*vflag*/) ifirst = dfirst[i]; nei = ednum[i]; iefirst = edfirst[i]; - eradi = enclosing_radius[i]; // reset vertex and edge forces @@ -354,10 +361,8 @@ void FixWallBodyPolygon::post_force(int /*vflag*/) // scale the force at both contacts - contact_forces(contact_list[m], j_a, x, v, angmom, f, torque, - vwall, facc); - contact_forces(contact_list[n], j_a, x, v, angmom, f, torque, - vwall, facc); + contact_forces(contact_list[m], j_a, x, v, angmom, f, torque, vwall, facc); + contact_forces(contact_list[n], j_a, x, v, angmom, f, torque, vwall, facc); done = 1; break; } @@ -370,11 +375,32 @@ void FixWallBodyPolygon::post_force(int /*vflag*/) // if there's only one contact, it should be handled here // since forces/torques have not been accumulated from vertex2wall() - contact_forces(contact_list[0], 1.0, x, v, angmom, f, torque, - vwall, facc); + contact_forces(contact_list[0], 1.0, x, v, angmom, f, torque, vwall, facc); } } // group bit } + + // update wall image information + int m = 0; + if (wallstyle == XPLANE) { + if (lo != -BIG) { + FixWall::update_image_plane(m, FixWall::XLO, wlo, imgparms, domain); + ++m; + } + if (hi != BIG) { + FixWall::update_image_plane(m, FixWall::XHI, whi, imgparms, domain); + ++m; + } + } else if (wallstyle == YPLANE) { + if (lo != -BIG) { + FixWall::update_image_plane(m, FixWall::YLO, wlo, imgparms, domain); + ++m; + } + if (hi != BIG) { + FixWall::update_image_plane(m, FixWall::YHI, whi, imgparms, domain); + ++m; + } + } } /* ---------------------------------------------------------------------- */ @@ -498,8 +524,7 @@ int FixWallBodyPolygon::vertex_against_wall(int i, double wall_pos, // compute the distance from the vertex xpi to the wall - mode = compute_distance_to_wall(xpi, rradi, wall_pos, side, - d, hi, contact); + mode = compute_distance_to_wall(xpi, rradi, wall_pos, side, d, hi, contact); if (mode == INVALID || mode == NONE) continue; @@ -532,14 +557,6 @@ int FixWallBodyPolygon::vertex_against_wall(int i, double wall_pos, fy = dely*fpair/rij; fz = delz*fpair/rij; - #ifdef _POLYGON_DEBUG - printf(" Interaction between vertex %d of %d and wall:", ni); - printf(" mode = %d; contact = %d; d = %f; rij = %f\n", - mode, contact, d, rij); - printf(" R = %f\n", R); - printf(" fpair = %f\n", fpair); - #endif - if (contact == 1) { // vertex ni of body i contacts with edge nj of body j @@ -564,10 +581,6 @@ int FixWallBodyPolygon::vertex_against_wall(int i, double wall_pos, discrete[ifirst+ni][4] = fy; discrete[ifirst+ni][5] = fz; - #ifdef _POLYGON_DEBUG - printf(" Stored forces at vertex and edge for accumulating later.\n"); - #endif - } else { // no contact // accumulate force and torque to the body directly @@ -602,11 +615,10 @@ int FixWallBodyPolygon::vertex_against_wall(int i, double wall_pos, EDGE if the tested vertex interacts with the wall ------------------------------------------------------------------------- */ -int FixWallBodyPolygon::compute_distance_to_wall(double* x0, double rradi, - double wall_pos, int side, double &d, double hi[3], int &contact) +int FixWallBodyPolygon::compute_distance_to_wall(double* x0, double rradi, double wall_pos, + int side, double &d, double hi[3], int &contact) { int mode; - double delxy; // h0 = position of the projection of x0 on the wall if (wallstyle == XPLANE) { @@ -617,11 +629,6 @@ int FixWallBodyPolygon::compute_distance_to_wall(double* x0, double rradi, hi[0] = x0[0]; hi[1] = wall_pos; hi[2] = x0[2]; - } else if (wallstyle == ZCYLINDER) { - delxy = sqrt(x0[0]*x0[0] + x0[1]*x0[1]); - hi[0] = x0[0]*cylradius/delxy; - hi[1] = x0[1]*cylradius/delxy; - hi[2] = x0[2]; } // distance from x0 to the wall = distance from x0 to hi @@ -738,13 +745,6 @@ void FixWallBodyPolygon::contact_forces(Contact& contact, double j_a, // accumulate forces to the vertex only facc[0] += fx; facc[1] += fy; facc[2] += fz; - - #ifdef _POLYGON_DEBUG - printf("From contact forces: vertex fx %f fy %f fz %f\n" - " torque body %d: %f %f %f\n", - discrete[ifirst+ni][3], discrete[ifirst+ni][4], discrete[ifirst+ni][5], - atom->tag[ibody],torque[ibody][0],torque[ibody][1],torque[ibody][2]); - #endif } /* ---------------------------------------------------------------------- @@ -820,3 +820,15 @@ void FixWallBodyPolygon::distance(const double* x2, const double* x1, + (x2[1] - x1[1]) * (x2[1] - x1[1]) + (x2[2] - x1[2]) * (x2[2] - x1[2])); } + +/* ---------------------------------------------------------------------- + provide graphics information to dump image to render wall as plane + data has been copied to dedicated storage during fix indent execution +------------------------------------------------------------------------- */ + +int FixWallBodyPolygon::image(int *&objs, double **&parms) +{ + objs = imgobjs; + parms = imgparms; + return numwalls; +} diff --git a/src/BODY/fix_wall_body_polygon.h b/src/BODY/fix_wall_body_polygon.h index 0fa4ee743ba..6772cd5183b 100644 --- a/src/BODY/fix_wall_body_polygon.h +++ b/src/BODY/fix_wall_body_polygon.h @@ -34,6 +34,8 @@ class FixWallBodyPolygon : public Fix { void post_force(int) override; void reset_dt() override; + int image(int *&, double **&) override; + struct Contact { int ibody, jbody; // body (i.e. atom) indices (not tags) int vertex; // vertex of the first polygon @@ -73,6 +75,12 @@ class FixWallBodyPolygon : public Fix { double *enclosing_radius; // enclosing radii for all bodies double *rounded_radius; // rounded radii for all bodies + // for dump image support + + int numwalls; + int *imgobjs; + double **imgparms; + void body2space(int); int vertex_against_wall(int ibody, double wall_pos, double **x, double **f, double **torque, diff --git a/src/BODY/fix_wall_body_polyhedron.cpp b/src/BODY/fix_wall_body_polyhedron.cpp index b42cc6843f1..c9710817574 100644 --- a/src/BODY/fix_wall_body_polyhedron.cpp +++ b/src/BODY/fix_wall_body_polyhedron.cpp @@ -23,7 +23,9 @@ #include "body_rounded_polyhedron.h" #include "domain.h" #include "error.h" +#include "fix_wall.h" #include "force.h" +#include "graphics.h" #include "math_const.h" #include "math_extra.h" #include "memory.h" @@ -37,27 +39,23 @@ using namespace FixConst; using namespace MathConst; enum{XPLANE=0,YPLANE=1,ZPLANE=2}; // XYZ PLANE need to be 0,1,2 -enum{HOOKE,HOOKE_HISTORY}; enum {INVALID=0,NONE=1,VERTEX=2}; enum {FAR=0,XLO,XHI,YLO,YHI,ZLO,ZHI}; -//#define _POLYHEDRON_DEBUG static constexpr int DELTA = 10000; static constexpr double EPSILON = 1.0e-3; // dimensionless threshold (dot products, end point checks) static constexpr double BIG = 1.0e20; -static constexpr int MAX_CONTACTS = 4; // maximum number of contacts for 2D models /* ---------------------------------------------------------------------- */ FixWallBodyPolyhedron::FixWallBodyPolyhedron(LAMMPS *lmp, int narg, char **arg) : - Fix(lmp, narg, arg) + Fix(lmp, narg, arg), imgobjs(nullptr), imgparms(nullptr) { - if (narg < 7) error->all(FLERR,"Illegal fix wall/body/polyhedron command"); + if (narg < 9) utils::missing_cmd_args(FLERR,"fix wall/body/polyhedron", error); if (!atom->body_flag) - error->all(FLERR,"Fix wall/body/polyhedron requires " - "atom style body/rounded/polyhedron"); + error->all(FLERR,"Fix wall/body/polyhedron requires atom style body/rounded/polyhedron"); restart_peratom = 1; create_attribute = 1; @@ -66,68 +64,92 @@ FixWallBodyPolyhedron::FixWallBodyPolyhedron(LAMMPS *lmp, int narg, char **arg) // wall/particle coefficients kn = utils::numeric(FLERR,arg[3],false,lmp); - + if (kn < 0.0) error->all(FLERR,3,"Illegal fix wall/body/polyhedron argument {}", kn); c_n = utils::numeric(FLERR,arg[4],false,lmp); + if (c_n < 0.0) + error->all(FLERR,4,"Illegal fix wall/body/polyhedron argument {}", c_n); if (strcmp(arg[5],"NULL") == 0) c_t = 0.5 * c_n; else c_t = utils::numeric(FLERR,arg[5],false,lmp); - - if (kn < 0.0 || c_n < 0.0 || c_t < 0.0) - error->all(FLERR,"Illegal fix wall/body/polyhedron command"); + if (c_t < 0.0) + error->all(FLERR,5,"Illegal fix wall/body/polyhedron argument {}", c_t); // wallstyle args - int iarg = 6; - if (strcmp(arg[iarg],"xplane") == 0) { - if (narg < iarg+3) error->all(FLERR,"Illegal fix wall/body/polyhedron command"); + numwalls = 0; + if (strcmp(arg[6],"xplane") == 0) { wallstyle = XPLANE; - if (strcmp(arg[iarg+1],"NULL") == 0) lo = -BIG; - else lo = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (strcmp(arg[iarg+2],"NULL") == 0) hi = BIG; - else hi = utils::numeric(FLERR,arg[iarg+2],false,lmp); - iarg += 3; - } else if (strcmp(arg[iarg],"yplane") == 0) { - if (narg < iarg+3) error->all(FLERR,"Illegal fix wall/body/polyhedron command"); + if (strcmp(arg[7],"NULL") == 0) { + lo = -BIG; + } else { + lo = utils::numeric(FLERR,arg[7],false,lmp); + ++numwalls; + } + if (strcmp(arg[8],"NULL") == 0) { + hi = BIG; + } else { + hi = utils::numeric(FLERR,arg[8],false,lmp); + ++numwalls; + } + + } else if (strcmp(arg[6],"yplane") == 0) { wallstyle = YPLANE; - if (strcmp(arg[iarg+1],"NULL") == 0) lo = -BIG; - else lo = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (strcmp(arg[iarg+2],"NULL") == 0) hi = BIG; - else hi = utils::numeric(FLERR,arg[iarg+2],false,lmp); - iarg += 3; - } else if (strcmp(arg[iarg],"zplane") == 0) { - if (narg < iarg+3) error->all(FLERR,"Illegal fix wall/body/polyhedron command"); + if (strcmp(arg[7],"NULL") == 0) { + lo = -BIG; + } else { + lo = utils::numeric(FLERR,arg[7],false,lmp); + ++numwalls; + } + if (strcmp(arg[8],"NULL") == 0) { + hi = BIG; + } else { + hi = utils::numeric(FLERR,arg[8],false,lmp); + ++numwalls; + } + + } else if (strcmp(arg[6],"zplane") == 0) { wallstyle = ZPLANE; - if (strcmp(arg[iarg+1],"NULL") == 0) lo = -BIG; - else lo = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (strcmp(arg[iarg+2],"NULL") == 0) hi = BIG; - else hi = utils::numeric(FLERR,arg[iarg+2],false,lmp); - iarg += 3; - } else error->all(FLERR,"Unknown wall style {}",arg[iarg]); + if (strcmp(arg[7],"NULL") == 0) { + lo = -BIG; + } else { + lo = utils::numeric(FLERR,arg[7],false,lmp); + ++numwalls; + } + if (strcmp(arg[8],"NULL") == 0) { + hi = BIG; + } else { + hi = utils::numeric(FLERR,arg[8],false,lmp); + ++numwalls; + } + } else error->all(FLERR, 6, "Unknown wall style {}",arg[6]); + + if ((wallstyle == XPLANE) && domain->xperiodic) + error->all(FLERR, 6, "Cannot use wall in periodic dimension"); + if ((wallstyle == YPLANE) && domain->yperiodic) + error->all(FLERR, 6, "Cannot use wall in periodic dimension"); + if ((wallstyle == ZPLANE) && domain->zperiodic) + error->all(FLERR, 6, "Cannot use wall in periodic dimension"); // check for trailing keyword/values wiggle = 0; - + int iarg = 9; while (iarg < narg) { if (strcmp(arg[iarg],"wiggle") == 0) { - if (iarg+4 > narg) error->all(FLERR,"Illegal fix wall/body/polyhedron command"); - if (strcmp(arg[iarg+1],"x") == 0) axis = 0; - else if (strcmp(arg[iarg+1],"y") == 0) axis = 1; - else if (strcmp(arg[iarg+1],"z") == 0) axis = 2; - else error->all(FLERR,"Illegal fix wall/body/polyhedron command"); + if (iarg+4 > narg) + utils::missing_cmd_args(FLERR,"fix wall/body/polyhedron wiggle", error); + + if (strcmp(arg[iarg+1],"x") == 0) axis = XPLANE; + else if (strcmp(arg[iarg+1],"y") == 0) axis = YPLANE; + else if (strcmp(arg[iarg+1],"z") == 0) axis = ZPLANE; + else error->all(FLERR, iarg+1, + "Illegal fix wall/body/polyhedron wiggle direction {}", arg[iarg+1]); amplitude = utils::numeric(FLERR,arg[iarg+2],false,lmp); period = utils::numeric(FLERR,arg[iarg+3],false,lmp); wiggle = 1; iarg += 4; - } else error->all(FLERR,"Illegal fix wall/body/polyhedron command"); + } else error->all(FLERR, iarg, "Unknown fix wall/body/polyhedron keyword {}", arg[iarg]); } - if (wallstyle == XPLANE && domain->xperiodic) - error->all(FLERR,"Cannot use wall in periodic dimension"); - if (wallstyle == YPLANE && domain->yperiodic) - error->all(FLERR,"Cannot use wall in periodic dimension"); - if (wallstyle == ZPLANE && domain->zperiodic) - error->all(FLERR,"Cannot use wall in periodic dimension"); - // setup oscillations if (wiggle) omega = 2.0*MY_PI / period; @@ -148,6 +170,29 @@ FixWallBodyPolyhedron::FixWallBodyPolyhedron(LAMMPS *lmp, int narg, char **arg) enclosing_radius = nullptr; rounded_radius = nullptr; + + // for rendering walls with dump image. + if (numwalls > 0) { + if (domain->dimension == 2) { + // one cylinder object per wall to draw in 2d + memory->create(imgobjs, numwalls, "fix_wall:imgobjs"); + memory->create(imgparms, numwalls, 8, "fix_wall:imgparms"); + for (int m = 0; m < numwalls; ++m) { + imgobjs[m] = Graphics::CYLINDER; + imgparms[m][0] = 1; // use color of first atom type by default + } + } else { + // two triangle objects per wall to draw in 3d + memory->create(imgobjs, 2 * numwalls, "fix_wall:imgobjs"); + memory->create(imgparms, 2 * numwalls, 10, "fix_wall:imgparms"); + for (int m = 0; m < numwalls; ++m) { + imgobjs[2 * m] = Graphics::TRIANGLE; + imgobjs[2 * m + 1] = Graphics::TRIANGLE; + imgparms[2 * m][0] = 1; // use color of first atom type by default + imgparms[2 * m + 1][0] = 1; // use color of first atom type by default + } + } + } } /* ---------------------------------------------------------------------- */ @@ -168,6 +213,9 @@ FixWallBodyPolyhedron::~FixWallBodyPolyhedron() memory->destroy(enclosing_radius); memory->destroy(rounded_radius); + + memory->destroy(imgobjs); + memory->destroy(imgparms); } /* ---------------------------------------------------------------------- */ @@ -187,17 +235,14 @@ void FixWallBodyPolyhedron::init() avec = dynamic_cast(atom->style_match("body")); if (!avec) - error->all(FLERR,"Pair body/rounded/polyhedron requires atom style body"); + error->all(FLERR,Error::NOLASTLINE,"Pair body/rounded/polyhedron requires atom style body"); if (strcmp(avec->bptr->style,"rounded/polyhedron") != 0) - error->all(FLERR,"Pair body/rounded/polyhedron requires " - "body style rounded/polyhedron"); + error->all(FLERR,Error::NOLASTLINE, + "Pair body/rounded/polyhedron requires body style rounded/polyhedron"); bptr = dynamic_cast(avec->bptr); - // set pairstyle from body/polyhedronular pair style - - if (force->pair_match("body/rounded/polyhedron",1)) - pairstyle = HOOKE; - else error->all(FLERR,"Fix wall/body/polyhedron is incompatible with Pair style"); + if (!force->pair_match("body/rounded/polyhedron",1)) + error->all(FLERR,Error::NOLASTLINE,"Fix wall/body/polyhedron is incompatible with Pair style"); } /* ---------------------------------------------------------------------- */ @@ -213,7 +258,7 @@ void FixWallBodyPolyhedron::setup(int vflag) void FixWallBodyPolyhedron::post_force(int /*vflag*/) { double vwall[3],dx,dy,dz,del1,del2,rsq,wall_pos; - int i,ni,npi,ifirst,nei,iefirst,side; + int i,ni,npi,ifirst,nei,iefirst; double facc[3]; // set position of wall to initial settings and velocity to 0.0 @@ -282,18 +327,15 @@ void FixWallBodyPolyhedron::post_force(int /*vflag*/) if (body[i] < 0) continue; dx = dy = dz = 0.0; - side = FAR; if (wallstyle == XPLANE) { del1 = x[i][0] - wlo; del2 = whi - x[i][0]; if (del1 < del2) { dx = del1; wall_pos = wlo; - side = XLO; } else { dx = -del2; wall_pos = whi; - side = XHI; } } else if (wallstyle == YPLANE) { del1 = x[i][1] - wlo; @@ -301,11 +343,9 @@ void FixWallBodyPolyhedron::post_force(int /*vflag*/) if (del1 < del2) { dy = del1; wall_pos = wlo; - side = YLO; } else { dy = -del2; wall_pos = whi; - side = YHI; } } else if (wallstyle == ZPLANE) { del1 = x[i][2] - wlo; @@ -313,11 +353,9 @@ void FixWallBodyPolyhedron::post_force(int /*vflag*/) if (del1 < del2) { dy = del1; wall_pos = wlo; - side = ZLO; } else { dy = -del2; wall_pos = whi; - side = ZHI; } } @@ -331,7 +369,7 @@ void FixWallBodyPolyhedron::post_force(int /*vflag*/) iefirst = edfirst[i]; if (npi == 1) { - sphere_against_wall(i, wall_pos, side, vwall, x, v, f, angmom, torque); + sphere_against_wall(i, wall_pos, vwall, x, v, f, angmom, torque); continue; } @@ -351,16 +389,41 @@ void FixWallBodyPolyhedron::post_force(int /*vflag*/) edge[iefirst+ni][5] = 0; } - int num_contacts; - Contact contact_list[MAX_CONTACTS]; - - num_contacts = 0; facc[0] = facc[1] = facc[2] = 0; - edge_against_wall(i, wall_pos, side, vwall, x, f, torque, - contact_list, num_contacts, facc); - + edge_against_wall(i, wall_pos, vwall, x); } // group bit } + + // update wall image information + int m = 0; + if (wallstyle == XPLANE) { + if (lo != -BIG) { + FixWall::update_image_plane(m, FixWall::XLO, wlo, imgparms, domain); + ++m; + } + if (hi != BIG) { + FixWall::update_image_plane(m, FixWall::XHI, whi, imgparms, domain); + ++m; + } + } else if (wallstyle == YPLANE) { + if (lo != -BIG) { + FixWall::update_image_plane(m, FixWall::YLO, wlo, imgparms, domain); + ++m; + } + if (hi != BIG) { + FixWall::update_image_plane(m, FixWall::YHI, whi, imgparms, domain); + ++m; + } + } else if (wallstyle == ZPLANE) { + if (lo != -BIG) { + FixWall::update_image_plane(m, FixWall::ZLO, wlo, imgparms, domain); + ++m; + } + if (hi != BIG) { + FixWall::update_image_plane(m, FixWall::ZHI, whi, imgparms, domain); + ++m; + } + } } /* ---------------------------------------------------------------------- */ @@ -475,9 +538,9 @@ void FixWallBodyPolyhedron::body2space(int i) torque = atoms' torques ---------------------------------------------------------------------- */ -int FixWallBodyPolyhedron::sphere_against_wall(int i, double wall_pos, - int /*side*/, double* vwall, double** x, double** v, double** f, - double** angmom, double** torque) +int FixWallBodyPolyhedron::sphere_against_wall(int i, double wall_pos,double* vwall, double** x, + double** v, double** f, double** angmom, + double** torque) { int mode; double rradi,hi[3],d,delx,dely,delz,R,fpair,fx,fy,fz; @@ -513,8 +576,7 @@ int FixWallBodyPolyhedron::sphere_against_wall(int i, double wall_pos, fy = dely*fpair/d; fz = delz*fpair/d; - contact_forces(i, 1.0, x[i], hi, delx, dely, delz, - fx, fy, fz, x, v, angmom, f, torque, vwall); + contact_forces(i, 1.0, x[i], delx, dely, delz, fx, fy, fz, x, v, angmom, f, torque, vwall); mode = VERTEX; } @@ -535,9 +597,7 @@ int FixWallBodyPolyhedron::sphere_against_wall(int i, double wall_pos, number of contacts of the edge to the wall (0, 1 or 2) ---------------------------------------------------------------------- */ -int FixWallBodyPolyhedron::edge_against_wall(int i, double wall_pos, - int side, double* vwall, double** x, double** /*f*/, double** /*torque*/, - Contact* /*contact_list*/, int &/*num_contacts*/, double* /*facc*/) +int FixWallBodyPolyhedron::edge_against_wall(int i, double wall_pos, double* vwall, double** x) { int ni, nei, contact; double rradi; @@ -550,7 +610,7 @@ int FixWallBodyPolyhedron::edge_against_wall(int i, double wall_pos, // loop through body i's edges for (ni = 0; ni < nei; ni++) - compute_distance_to_wall(i, ni, x[i], rradi, wall_pos, side, vwall, contact); + compute_distance_to_wall(i, ni, x[i], rradi, wall_pos, vwall, contact); return contact; } @@ -571,9 +631,9 @@ int FixWallBodyPolyhedron::edge_against_wall(int i, double wall_pos, VERTEX if the tested vertex interacts with the wall ------------------------------------------------------------------------- */ -int FixWallBodyPolyhedron::compute_distance_to_wall(int ibody, int edge_index, - double *xmi, double rounded_radius_i, double wall_pos, - int /*side*/, double* vwall, int &contact) +int FixWallBodyPolyhedron::compute_distance_to_wall(int ibody, int edge_index, double *xmi, + double rounded_radius_i, double wall_pos, + double* vwall, int &contact) { int mode,ifirst,iefirst,npi1,npi2; double d1,d2,xpi1[3],xpi2[3],hi[3]; @@ -633,8 +693,7 @@ int FixWallBodyPolyhedron::compute_distance_to_wall(int ibody, int edge_index, fy = dely*fpair/d1; fz = delz*fpair/d1; - contact_forces(ibody, j_a, xpi1, hi, delx, dely, delz, - fx, fy, fz, x, v, angmom, f, torque, vwall); + contact_forces(ibody, j_a, xpi1, delx, dely, delz, fx, fy, fz, x, v, angmom, f, torque, vwall); discrete[ifirst+npi1][6] = 1; contact++; mode = VERTEX; @@ -668,8 +727,7 @@ int FixWallBodyPolyhedron::compute_distance_to_wall(int ibody, int edge_index, fy = dely*fpair/d2; fz = delz*fpair/d2; - contact_forces(ibody, j_a, xpi2, hi, delx, dely, delz, - fx, fy, fz, x, v, angmom, f, torque, vwall); + contact_forces(ibody, j_a, xpi2, delx, dely, delz, fx, fy, fz, x, v, angmom, f, torque, vwall); discrete[ifirst+npi2][6] = 1; contact++; mode = VERTEX; @@ -686,10 +744,10 @@ int FixWallBodyPolyhedron::compute_distance_to_wall(int ibody, int edge_index, ft = tangential friction component (-c_t * v_t) ------------------------------------------------------------------------- */ -void FixWallBodyPolyhedron::contact_forces(int ibody, - double j_a, double *xi, double * /*xj*/, double delx, double dely, double delz, - double fx, double fy, double fz, double** x, double** v, double** angmom, - double** f, double** torque, double* vwall) +void FixWallBodyPolyhedron::contact_forces(int ibody, double j_a, double *xi, double delx, + double dely, double delz, double fx, double fy, + double fz, double** x, double** v, double** angmom, + double** f, double** torque, double* vwall) { int ibonus; double fxt,fyt,fzt,rsq,rsqinv; @@ -753,15 +811,6 @@ void FixWallBodyPolyhedron::contact_forces(int ibody, f[ibody][1] += fy; f[ibody][2] += fz; sum_torque(x[ibody], xi, fx, fy, fz, torque[ibody]); - - #ifdef _POLYHEDRON_DEBUG - printf("From contact forces: vertex fx %f fy %f fz %f\n" - " torque body %d: %f %f %f\n" - " torque body %d: %f %f %f\n", - fxt, fyt, fzt, - atom->tag[ibody],torque[ibody][0],torque[ibody][1],torque[ibody][2], - atom->tag[jbody],torque[jbody][0],torque[jbody][1],torque[jbody][2]); - #endif } /* ---------------------------------------------------------------------- @@ -864,8 +913,7 @@ void FixWallBodyPolyhedron::contact_forces(Contact& contact, double j_a, 2 contacts ------------------------------------------------------------------------- */ -double FixWallBodyPolyhedron::contact_separation(const Contact& c1, - const Contact& c2) +double FixWallBodyPolyhedron::contact_separation(const Contact& c1, const Contact& c2) { double x1 = c1.xv[0]; double y1 = c1.xv[1]; @@ -908,9 +956,8 @@ void FixWallBodyPolyhedron::sum_torque(double* xm, double *x, double fx, vi = vcm + omega ^ (p - xcm) ------------------------------------------------------------------------- */ -void FixWallBodyPolyhedron::total_velocity(double* p, double *xcm, - double* vcm, double *angmom, double *inertia, - double *quat, double* vi) +void FixWallBodyPolyhedron::total_velocity(double* p, double *xcm, double* vcm, double *angmom, + double *inertia, double *quat, double* vi) { double r[3],omega[3],ex_space[3],ey_space[3],ez_space[3]; r[0] = p[0] - xcm[0]; @@ -926,9 +973,21 @@ void FixWallBodyPolyhedron::total_velocity(double* p, double *xcm, /* ---------------------------------------------------------------------- */ -void FixWallBodyPolyhedron::distance(const double* x2, const double* x1, - double& r) { +void FixWallBodyPolyhedron::distance(const double* x2, const double* x1, double& r) { r = sqrt((x2[0] - x1[0]) * (x2[0] - x1[0]) + (x2[1] - x1[1]) * (x2[1] - x1[1]) + (x2[2] - x1[2]) * (x2[2] - x1[2])); } + +/* ---------------------------------------------------------------------- + provide graphics information to dump image to render wall as plane + data has been copied to dedicated storage during fix indent execution +------------------------------------------------------------------------- */ + +int FixWallBodyPolyhedron::image(int *&objs, double **&parms) +{ + objs = imgobjs; + parms = imgparms; + if (domain->dimension == 2) return numwalls; + return 2*numwalls; +} diff --git a/src/BODY/fix_wall_body_polyhedron.h b/src/BODY/fix_wall_body_polyhedron.h index 2cd80e2947e..b7bcc4ea7ec 100644 --- a/src/BODY/fix_wall_body_polyhedron.h +++ b/src/BODY/fix_wall_body_polyhedron.h @@ -34,6 +34,8 @@ class FixWallBodyPolyhedron : public Fix { void post_force(int) override; void reset_dt() override; + int image(int *&, double **&) override; + struct Contact { int ibody, jbody; // body (i.e. atom) indices (not tags) int vertex; // vertex of the first polygon @@ -44,9 +46,9 @@ class FixWallBodyPolyhedron : public Fix { }; protected: - int wallstyle, pairstyle, wiggle, axis; + int wallstyle, wiggle, axis; double kn, c_n, c_t; - double lo, hi, cylradius; + double lo, hi; double amplitude, period, omega; double dt; int time_origin; @@ -78,19 +80,26 @@ class FixWallBodyPolyhedron : public Fix { double *enclosing_radius; // enclosing radii for all bodies double *rounded_radius; // rounded radii for all bodies + // dump image data + + int numwalls; + int *imgobjs; + double **imgparms; + + // store particle interactions + void body2space(int); - int edge_against_wall(int ibody, double wall_pos, int side, double *vwall, double **x, double **f, - double **torque, Contact *contact_list, int &num_contacts, double *facc); - int sphere_against_wall(int i, double wall_pos, int side, double *vwall, double **x, double **v, - double **f, double **angmom, double **torque); + int edge_against_wall(int ibody, double wall_pos, double *vwall, double **x); + int sphere_against_wall(int i, double wall_pos, double *vwall, double **x, double **v, double **f, + double **angmom, double **torque); int compute_distance_to_wall(int ibody, int edge_index, double *xmi, double rounded_radius_i, - double wall_pos, int side, double *vwall, int &contact); + double wall_pos, double *vwall, int &contact); double contact_separation(const Contact &c1, const Contact &c2); - void contact_forces(int ibody, double j_a, double *xi, double *xj, double delx, double dely, - double delz, double fx, double fy, double fz, double **x, double **v, - double **angmom, double **f, double **torque, double *vwall); + void contact_forces(int ibody, double j_a, double *xi, double delx, double dely, double delz, + double fx, double fy, double fz, double **x, double **v, double **angmom, + double **f, double **torque, double *vwall); void contact_forces(Contact &contact, double j_a, double **x, double **v, double **angmom, double **f, double **torque, double *vwall, double *facc); diff --git a/src/COLVARS/fix_colvars.cpp b/src/COLVARS/fix_colvars.cpp index f71ca4677ea..3063b133680 100644 --- a/src/COLVARS/fix_colvars.cpp +++ b/src/COLVARS/fix_colvars.cpp @@ -445,7 +445,8 @@ void FixColvars::setup(int vflag) { const tagint * const tag = atom->tag; const int * const type = atom->type; - int i,nme,tmp,ndata; + int i,nme,ndata; + int tmp = 0; const auto nlocal = atom->nlocal; const auto me = comm->me; @@ -649,7 +650,8 @@ void FixColvars::post_force(int /*vflag*/) MPI_Status status; MPI_Request request; - int tmp, ndata; + int tmp = 0; + int ndata =0; if (me == 0) { @@ -803,7 +805,8 @@ void FixColvars::end_of_step() MPI_Status status; MPI_Request request; - int tmp, ndata; + int tmp = 0; + int ndata = 0; if (comm->me == 0) { diff --git a/src/GRANULAR/fix_wall_gran.cpp b/src/GRANULAR/fix_wall_gran.cpp index 5dd8d6a9fc1..54e27704d25 100644 --- a/src/GRANULAR/fix_wall_gran.cpp +++ b/src/GRANULAR/fix_wall_gran.cpp @@ -23,9 +23,9 @@ #include "granular_model.h" #include "gran_sub_mod.h" #include "domain.h" -#include "dump_image.h" #include "error.h" #include "fix_wall.h" +#include "graphics.h" #include "input.h" #include "math_const.h" #include "math_extra.h" @@ -298,13 +298,14 @@ FixWallGran::FixWallGran(LAMMPS *lmp, int narg, char **arg) : time_origin = update->ntimestep; // for rendering walls with dump image. - if ((wallstyle == XPLANE) || (wallstyle == YPLANE) || (wallstyle == ZPLANE)) { + + if (numwalls > 0) { if (domain->dimension == 2) { // one cylinder object per wall to draw in 2d memory->create(imgobjs, numwalls, "fix_wall:imgobjs"); memory->create(imgparms, numwalls, 8, "fix_wall:imgparms"); for (int m = 0; m < numwalls; ++m) { - imgobjs[m] = DumpImage::CYLINDER; + imgobjs[m] = Graphics::CYLINDER; imgparms[m][0] = 1; // use color of first atom type by default } } else { @@ -312,8 +313,8 @@ FixWallGran::FixWallGran(LAMMPS *lmp, int narg, char **arg) : memory->create(imgobjs, 2 * numwalls, "fix_wall:imgobjs"); memory->create(imgparms, 2 * numwalls, 10, "fix_wall:imgparms"); for (int m = 0; m < numwalls; ++m) { - imgobjs[2 * m] = DumpImage::TRIANGLE; - imgobjs[2 * m + 1] = DumpImage::TRIANGLE; + imgobjs[2 * m] = Graphics::TRIANGLE; + imgobjs[2 * m + 1] = Graphics::TRIANGLE; imgparms[2 * m][0] = 1; // use color of first atom type by default imgparms[2 * m + 1][0] = 1; // use color of first atom type by default } @@ -417,7 +418,7 @@ void FixWallGran::setup(int vflag) void FixWallGran::post_force(int /*vflag*/) { int i,j,n; - double dx,dy,dz,del1,del2,delxy,delr,rwall,meff; + double dx,dy,dz,del1,del2,rwall,meff; double *forces, *torquesi; double vwall[3]; double w0[3] = {0.0}; diff --git a/src/dump_image.cpp b/src/GRAPHICS/dump_image.cpp similarity index 64% rename from src/dump_image.cpp rename to src/GRAPHICS/dump_image.cpp index 86130ca87a7..f714f5278e3 100644 --- a/src/dump_image.cpp +++ b/src/GRAPHICS/dump_image.cpp @@ -18,6 +18,7 @@ #include "atom.h" #include "atom_vec.h" #include "atom_vec_body.h" +#include "atom_vec_ellipsoid.h" #include "atom_vec_line.h" #include "atom_vec_tri.h" #include "body.h" @@ -27,9 +28,11 @@ #include "error.h" #include "fix.h" #include "force.h" +#include "graphics.h" #include "grid2d.h" #include "grid3d.h" #include "image.h" +#include "image_objects.h" #include "input.h" #include "math_const.h" #include "math_extra.h" @@ -46,6 +49,7 @@ #include "region_cone.h" #include "region_cylinder.h" #include "region_ellipsoid.h" +#include "region_plane.h" #include "region_prism.h" #include "region_sphere.h" #include "thermo.h" @@ -53,239 +57,24 @@ #include "update.h" #include "variable.h" -#include #include #include #include // clang-format on -// helper functions for generating triangle meshes - -namespace { - -using LAMMPS_NS::MathConst::MY_2PI; -constexpr int RESOLUTION = 50; -constexpr double RADINC = MY_2PI / RESOLUTION; - -using vec3 = std::array; -using triangle = std::array; - -vec3 vecadd(const vec3 &a, const vec3 &b) -{ - vec3 sum(a); - sum[0] += b[0]; - sum[1] += b[1]; - sum[2] += b[2]; - return sum; -} - -vec3 vecnorm(const vec3 &a) -{ - vec3 norm(a); - const double val = a[0] * a[0] + a[1] * a[1] + a[2] * a[2]; - double scale = 1.0; - if (val > 0.0) scale = 1.0 / sqrt(val); - norm[0] *= scale; - norm[1] *= scale; - norm[2] *= scale; - return norm; -} - -double radscale(const double *radius, const vec3 &pos) -{ - return sqrt(1.0 / - (pos[0] / radius[0] * pos[0] / radius[0] + pos[1] / radius[1] * pos[1] / radius[1] + - pos[2] / radius[2] * pos[2] / radius[2])); -} - -// refine the list of triangles. -// each triangle is replaced by 4 triangles with positions on the surface of a unit sphere -std::vector refine_triangle_list(const std::vector &inlist) -{ - std::vector outlist; - for (const auto &tri : inlist) { - vec3 posa = vecnorm(vecadd(tri[0], tri[2])); - vec3 posb = vecnorm(vecadd(tri[0], tri[1])); - vec3 posc = vecnorm(vecadd(tri[1], tri[2])); - outlist.push_back({tri[0], posb, posa}); - outlist.push_back({posb, tri[1], posc}); - outlist.push_back({posa, posb, posc}); - outlist.push_back({posa, posc, tri[2]}); - } - return outlist; -} - -void scale_and_displace_triangle(triangle &tri, const double *radius, const vec3 &offs) -{ - // scale and displace - for (int i = 0; i < 3; ++i) { - auto &t = tri[i]; - const auto scale = radscale(radius, t); - t[0] = t[0] * scale + offs[0]; - t[1] = t[1] * scale + offs[1]; - t[2] = t[2] * scale + offs[2]; - } -} - -void ellipsoid2wireframe(LAMMPS_NS::Image *img, int level, const double *color, double diameter, - const double *center, const double *radius, LAMMPS_NS::Region *reg) -{ - vec3 offset = {center[0], center[1], center[2]}; - - // define edges of an octahedron - vec3 pos1 = {-1.0, 0.0, 0.0}; - vec3 pos2 = {1.0, 0.0, 0.0}; - vec3 pos3 = {0.0, -1.0, 0.0}; - vec3 pos4 = {0.0, 1.0, 0.0}; - vec3 pos5 = {0.0, 0.0, -1.0}; - vec3 pos6 = {0.0, 0.0, 1.0}; - - // define level 1 octahedron triangle mesh - std::vector trilist = {{pos1, pos4, pos5}, {pos5, pos4, pos2}, {pos2, pos4, pos6}, - {pos6, pos4, pos1}, {pos5, pos3, pos1}, {pos2, pos3, pos5}, - {pos6, pos3, pos2}, {pos1, pos3, pos6}}; - - // draw level 1 triangle mesh - if (level <= 1) { - for (auto &tri : trilist) { - scale_and_displace_triangle(tri, radius, offset); - reg->forward_transform(tri[0][0], tri[0][1], tri[0][2]); - reg->forward_transform(tri[1][0], tri[1][1], tri[1][2]); - reg->forward_transform(tri[2][0], tri[2][1], tri[2][2]); - img->draw_cylinder(tri[0].data(), tri[1].data(), color, diameter, 3); - img->draw_cylinder(tri[0].data(), tri[2].data(), color, diameter, 3); - img->draw_cylinder(tri[1].data(), tri[2].data(), color, diameter, 3); - } - } - - if (level >= 2) { - trilist = refine_triangle_list(trilist); - - if (level == 2) { - for (auto &tri : trilist) { - scale_and_displace_triangle(tri, radius, offset); - reg->forward_transform(tri[0][0], tri[0][1], tri[0][2]); - reg->forward_transform(tri[1][0], tri[1][1], tri[1][2]); - reg->forward_transform(tri[2][0], tri[2][1], tri[2][2]); - img->draw_cylinder(tri[0].data(), tri[1].data(), color, diameter, 3); - img->draw_cylinder(tri[0].data(), tri[2].data(), color, diameter, 3); - img->draw_cylinder(tri[1].data(), tri[2].data(), color, diameter, 3); - } - } - } - - if (level >= 3) { - trilist = refine_triangle_list(trilist); - - if (level == 3) { - for (auto &tri : trilist) { - scale_and_displace_triangle(tri, radius, offset); - reg->forward_transform(tri[0][0], tri[0][1], tri[0][2]); - reg->forward_transform(tri[1][0], tri[1][1], tri[1][2]); - reg->forward_transform(tri[2][0], tri[2][1], tri[2][2]); - img->draw_cylinder(tri[0].data(), tri[1].data(), color, diameter, 3); - img->draw_cylinder(tri[0].data(), tri[2].data(), color, diameter, 3); - img->draw_cylinder(tri[1].data(), tri[2].data(), color, diameter, 3); - } - } - } - - if (level == 4) { - trilist = refine_triangle_list(trilist); - - for (auto &tri : trilist) { - scale_and_displace_triangle(tri, radius, offset); - reg->forward_transform(tri[0][0], tri[0][1], tri[0][2]); - reg->forward_transform(tri[1][0], tri[1][1], tri[1][2]); - reg->forward_transform(tri[2][0], tri[2][1], tri[2][2]); - img->draw_cylinder(tri[0].data(), tri[1].data(), color, diameter, 3); - img->draw_cylinder(tri[0].data(), tri[2].data(), color, diameter, 3); - img->draw_cylinder(tri[1].data(), tri[2].data(), color, diameter, 3); - } - } -} - -void ellipsoid2filled(LAMMPS_NS::Image *img, int level, const double *color, const double *center, - const double *radius, LAMMPS_NS::Region *reg, double opacity) -{ - vec3 offset = {center[0], center[1], center[2]}; - - // define edges of an octahedron - vec3 pos1 = {-1.0, 0.0, 0.0}; - vec3 pos2 = {1.0, 0.0, 0.0}; - vec3 pos3 = {0.0, -1.0, 0.0}; - vec3 pos4 = {0.0, 1.0, 0.0}; - vec3 pos5 = {0.0, 0.0, -1.0}; - vec3 pos6 = {0.0, 0.0, 1.0}; - - // define level 1 octahedron triangle mesh - std::vector trilist = {{pos1, pos4, pos5}, {pos5, pos4, pos2}, {pos2, pos4, pos6}, - {pos6, pos4, pos1}, {pos5, pos3, pos1}, {pos2, pos3, pos5}, - {pos6, pos3, pos2}, {pos1, pos3, pos6}}; - - if (level <= 1) { - for (auto &tri : trilist) { - scale_and_displace_triangle(tri, radius, offset); - reg->forward_transform(tri[0][0], tri[0][1], tri[0][2]); - reg->forward_transform(tri[1][0], tri[1][1], tri[1][2]); - reg->forward_transform(tri[2][0], tri[2][1], tri[2][2]); - img->draw_triangle(tri[0].data(), tri[1].data(), tri[2].data(), color, opacity); - } - } - - // refine the list of triangles - if (level >= 2) { - trilist = refine_triangle_list(trilist); - - if (level == 2) { - for (auto &tri : trilist) { - scale_and_displace_triangle(tri, radius, offset); - reg->forward_transform(tri[0][0], tri[0][1], tri[0][2]); - reg->forward_transform(tri[1][0], tri[1][1], tri[1][2]); - reg->forward_transform(tri[2][0], tri[2][1], tri[2][2]); - img->draw_triangle(tri[0].data(), tri[1].data(), tri[2].data(), color, opacity); - } - } - } - - if (level >= 3) { - trilist = refine_triangle_list(trilist); - - if (level == 3) { - for (auto &tri : trilist) { - scale_and_displace_triangle(tri, radius, offset); - reg->forward_transform(tri[0][0], tri[0][1], tri[0][2]); - reg->forward_transform(tri[1][0], tri[1][1], tri[1][2]); - reg->forward_transform(tri[2][0], tri[2][1], tri[2][2]); - img->draw_triangle(tri[0].data(), tri[1].data(), tri[2].data(), color, opacity); - } - } - } - - if (level == 4) { - trilist = refine_triangle_list(trilist); - - for (auto &tri : trilist) { - scale_and_displace_triangle(tri, radius, offset); - reg->forward_transform(tri[0][0], tri[0][1], tri[0][2]); - reg->forward_transform(tri[1][0], tri[1][1], tri[1][2]); - reg->forward_transform(tri[2][0], tri[2][1], tri[2][2]); - img->draw_triangle(tri[0].data(), tri[1].data(), tri[2].data(), color, opacity); - } - } -} -} // namespace - using namespace LAMMPS_NS; using MathConst::DEG2RAD; +using namespace ImageObjects; -static constexpr double BIG = 1.0e20; - -enum { NUMERIC, ATOM, TYPE, ELEMENT, ATTRIBUTE, CONSTANT }; +namespace { +constexpr double BIG = 1.0e20; +enum { NUMERIC, ATOM, TYPE, ELEMENT, ATTRIBUTE, CONSTANT, INDEX }; enum { STATIC, DYNAMIC }; enum { NO = 0, YES = 1, AUTO = 2 }; enum { FILLED, FRAME, POINTS, TRANSPARENT }; +} // namespace +// clang-format off /* ---------------------------------------------------------------------- */ @@ -296,7 +85,8 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : colorelement(nullptr), bcolortype(nullptr), aopacity(nullptr), bopacity(nullptr), grid2d(nullptr), grid3d(nullptr), id_grid_compute(nullptr), id_grid_fix(nullptr), grid_compute(nullptr), grid_fix(nullptr), gbuf(nullptr), avec_line(nullptr), avec_tri(nullptr), - avec_body(nullptr), image(nullptr), chooseghost(nullptr), bufcopy(nullptr) + avec_ellipsoid(nullptr), avec_body(nullptr), image(nullptr), chooseghost(nullptr), + bufcopy(nullptr) { if (binary || multiproc) error->all(FLERR, 4, "Invalid dump image filename {}", filename); @@ -309,7 +99,6 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : has_id = true; - // clang-format off // set filetype based on filename suffix if (utils::strmatch(filename, "\\.jpg$") || utils::strmatch(filename, "\\.JPG$") @@ -357,7 +146,7 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : atomflag = YES; gridflag = NO; - lineflag = triflag = bodyflag = NO; + lineflag = triflag = bodyflag = ellipsoidflag = NO; bcolor = ATOM; bdiam = NUMERIC; @@ -393,7 +182,7 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : adiam = NUMERIC; adiamvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); if (adiamvalue <= 0.0) - error->all(FLERR, iarg+1, "Illegal dump image adiam value {}", adiamvalue); + error->all(FLERR, iarg + 1, "Illegal dump image adiam value {}", adiamvalue); iarg += 2; } else if (strcmp(arg[iarg],"autobond") == 0) { @@ -471,11 +260,30 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : tdiamvalue = utils::numeric(FLERR,arg[iarg+3],false,lmp); iarg += 4; + } else if (strcmp(arg[iarg],"ellipsoid") == 0) { + if (iarg+5 > narg) utils::missing_cmd_args(FLERR,"dump image ellipsoid", error); + ellipsoidflag = YES; + if (strcmp(arg[iarg+1],"type") == 0) ecolor = TYPE; + else error->all(FLERR, iarg+1, "Dump image ellipsoid only supports color by type"); + estyle = utils::inumeric(FLERR,arg[iarg+2],false,lmp); + if ((estyle < 0) || (estyle > 3)) + error->all(FLERR, iarg+2, "Dump image ellipsoid only supports style setting 1, 2, or 3"); + elevel = utils::inumeric(FLERR,arg[iarg+3],false,lmp); + if (elevel == 0) elevel = 4; // default setting + if (elevel > 6) + error->all(FLERR, iarg+3, "Dump image ellipsoid mesh refinement level is too large"); + ediamvalue = utils::numeric(FLERR,arg[iarg+4],false,lmp); + iarg += 5; + } else if (strcmp(arg[iarg],"body") == 0) { if (iarg+4 > narg) utils::missing_cmd_args(FLERR,"dump image body", error); bodyflag = YES; if (strcmp(arg[iarg+1],"type") == 0) bodycolor = TYPE; - else error->all(FLERR, iarg+1, "Dump image body only support color by type"); + else if (strcmp(arg[iarg+1],"index") == 0) bodycolor = INDEX; + else + error->all(FLERR, iarg+1, "Dump image body only supports color by type or index"); + if (acolor != TYPE) + error->all(FLERR, iarg+1, "Must color atoms by type with body particles"); bodyflag1 = utils::numeric(FLERR,arg[iarg+2],false,lmp); bodyflag2 = utils::numeric(FLERR,arg[iarg+3],false,lmp); iarg += 4; @@ -492,7 +300,7 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : else error->all(FLERR, iarg+2, "Unsupported color style for dump image fix {}", arg[iarg+2]); double fixflag1 = utils::numeric(FLERR,arg[iarg+3],false,lmp); double fixflag2 = utils::numeric(FLERR,arg[iarg+4],false,lmp); - fixes.emplace_back(id_fix, fixptr, fixcolor, fixflag1, fixflag2, image->color2rgb("red")); + fixes.emplace_back(id_fix, fixptr, fixcolor, fixflag1, fixflag2, image->color2rgb("white")); iarg += 5; } else if (strcmp(arg[iarg],"region") == 0) { @@ -529,10 +337,11 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : iarg += 2; } else if (drawstyle == TRANSPARENT) { if (iarg+5 > narg) utils::missing_cmd_args(FLERR,"dump image region transparent", error); - opacity = utils::numeric(FLERR, arg[iarg+5], false, lmp); + opacity = utils::numeric(FLERR, arg[iarg+4], false, lmp); if ((opacity < 0.0) || (opacity > 1.0)) - error->all(FLERR, iarg+5, "Dump image region opacity must be in the range 0.0 to 1.0"); - iarg += 2; + error->all(FLERR, iarg+4, "Dump image region opacity must be in the range 0.0 to 1.0"); + + ++iarg; } iarg += 4; regions.emplace_back(regptr->id, regptr, regcolor, drawstyle, framediam, opacity, npoints); @@ -575,7 +384,7 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : if (iarg+5 > narg) utils::missing_cmd_args(FLERR,"dump image center", error); if (strcmp(arg[iarg+1],"s") == 0) cflag = STATIC; else if (strcmp(arg[iarg+1],"d") == 0) cflag = DYNAMIC; - else error->all(FLERR,"Illegal dump image command"); + else error->all(FLERR, iarg+1, "Unknown dump image center style {}", arg[iarg+1]); if (utils::strmatch(arg[iarg+2],"^v_")) { delete[] cxstr; cxstr = utils::strdup(arg[iarg+2]+2); @@ -616,7 +425,7 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : zoomstr = utils::strdup(arg[iarg+1]+2); } else { double zoom = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (zoom <= 0.0) error->all(FLERR,"Illegal dump image command"); + if (zoom <= 0.0) error->all(FLERR, iarg+1, "Invail dump image zoom value {}", zoom); image->zoom = zoom; } iarg += 2; @@ -625,7 +434,7 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : if (iarg+3 > narg) utils::missing_cmd_args(FLERR,"dump image box", error); boxflag = utils::logical(FLERR,arg[iarg+1],false,lmp); boxdiam = utils::numeric(FLERR,arg[iarg+2],false,lmp); - if (boxdiam < 0.0) error->all(FLERR,"Illegal dump image command"); + if (boxdiam < 0.0) error->all(FLERR, iarg+2, "Invalid dump image box diameter {}", boxdiam); iarg += 3; } else if (strcmp(arg[iarg],"axes") == 0) { @@ -633,22 +442,25 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : axesflag = utils::logical(FLERR,arg[iarg+1],false,lmp); axeslen = utils::numeric(FLERR,arg[iarg+2],false,lmp); axesdiam = utils::numeric(FLERR,arg[iarg+3],false,lmp); - if (axeslen < 0.0 || axesdiam < 0.0) - error->all(FLERR,"Illegal dump image command"); + if (axeslen < 0.0) + error->all(FLERR, iarg+2, "Invalid dump image axes length {}", axeslen); + if (axesdiam < 0.0) + error->all(FLERR,"Invalid dump image axes diameter {}", axesdiam); iarg += 4; } else if (strcmp(arg[iarg],"subbox") == 0) { if (iarg+3 > narg) utils::missing_cmd_args(FLERR,"dump image subbox", error); subboxflag = utils::logical(FLERR,arg[iarg+1],false,lmp); subboxdiam = utils::numeric(FLERR,arg[iarg+2],false,lmp); - if (subboxdiam < 0.0) error->all(FLERR,"Illegal dump image command"); + if (subboxdiam < 0.0) + error->all(FLERR,"Invalid dump image subbox diameter {}", subboxdiam); iarg += 3; } else if (strcmp(arg[iarg],"shiny") == 0) { if (iarg+2 > narg) utils::missing_cmd_args(FLERR,"dump image shiny", error); double shiny = utils::numeric(FLERR,arg[iarg+1],false,lmp); if (shiny < 0.0 || shiny > 1.0) - error->all(FLERR,"Illegal dump image command"); + error->all(FLERR, iarg+1, "Invalid dump image shiny value {}", shiny); image->shiny = shiny; iarg += 2; @@ -673,15 +485,15 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : if (iarg+4 > narg) utils::missing_cmd_args(FLERR,"dump image ssao", error); image->ssao = utils::logical(FLERR,arg[iarg+1],false,lmp); int seed = utils::inumeric(FLERR,arg[iarg+2],false,lmp); - if (seed <= 0) error->all(FLERR, iarg + 2, "Illegal dump image ssao seed {}", seed); + if (seed <= 0) error->all(FLERR, iarg + 2, "Invalid dump image ssao seed {}", seed); image->seed = seed; double ssaoint = utils::numeric(FLERR,arg[iarg+3],false,lmp); if (ssaoint < 0.0 || ssaoint > 1.0) - error->all(FLERR,"Illegal dump image command"); + error->all(FLERR, iarg+3, "Invalid dump image ssao intensity value {}", ssaoint); image->ssaoint = ssaoint; iarg += 4; - } else error->all(FLERR,"Unknown dump image keyword {}", arg[iarg]); + } else error->all(FLERR, iarg, "Unknown dump image keyword {}", arg[iarg]); } // error checks and setup for lineflag, triflag, bodyflag @@ -696,6 +508,12 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : if (!avec_tri) error->all(FLERR, Error::NOLASTLINE, "Dump image tri requires atom style tri"); } + + if (ellipsoidflag) { + avec_ellipsoid = dynamic_cast(atom->style_match("ellipsoid")); + if (!avec_ellipsoid) + error->all(FLERR, Error::NOLASTLINE, "Dump image ellipsoid requires atom style ellipsoid"); + } if (bodyflag) { avec_body = dynamic_cast(atom->style_match("body")); if (!avec_body) @@ -703,7 +521,7 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : } extraflag = 0; - if (lineflag || triflag || bodyflag) extraflag = 1; + if (lineflag || triflag || ellipsoidflag || bodyflag) extraflag = 1; // allocate image buffer now that image size is known @@ -898,20 +716,16 @@ void DumpImage::init_style() // set up type -> element mapping - if (atomflag && acolor == ELEMENT) { - for (int i = 1; i <= ntypes; i++) { - colorelement[i] = image->element2color(typenames[i]); - if (colorelement[i] == nullptr) - error->all(FLERR, Error::NOLASTLINE, "Invalid dump image element name"); - } + for (int i = 1; i <= ntypes; i++) { + colorelement[i] = image->element2color(typenames[i]); + if (colorelement[i] == nullptr) + error->all(FLERR, Error::NOLASTLINE, "Invalid dump image element name"); } - if (atomflag && adiam == ELEMENT) { - for (int i = 1; i <= ntypes; i++) { - diamelement[i] = image->element2diam(typenames[i]); - if (diamelement[i] == 0.0) - error->all(FLERR, Error::NOLASTLINE, "Invalid dump image element name"); - } + for (int i = 1; i <= ntypes; i++) { + diamelement[i] = image->element2diam(typenames[i]); + if (diamelement[i] == 0.0) + error->all(FLERR, Error::NOLASTLINE, "Invalid dump image element name"); } if (bondflag == AUTO) { @@ -940,7 +754,7 @@ void DumpImage::init_style() // check if fix data for dump image is available at the required steps. int nfreq = fixptr->global_freq; - if ((nfreq == 0) || (nevery % nfreq)) + if ((update->ntimestep != 0) && ((nfreq == 0) || (nevery % nfreq))) error->all(FLERR, Error::NOLASTLINE, "Dump {} and fix {} are not executed at compatible timesteps {}", style, fixptr->style, utils::errorurl(7)); @@ -1230,7 +1044,7 @@ void DumpImage::create_image() { int i,j,k,m,n,itype,atom1,atom2,imol,iatom,btype,ibonus,drawflag; tagint tagprev; - double diameter,delx,dely,delz,opacity; + double diameter,delx,dely,delz; int *bodyvec; double **bodyarray; double *color,*color1,*color2; @@ -1244,6 +1058,7 @@ void DumpImage::create_image() double **x = atom->x; int *line = atom->line; int *tri = atom->tri; + int *ellipsoid = atom->ellipsoid; int *body = atom->body; m = 0; @@ -1278,6 +1093,7 @@ void DumpImage::create_image() if (extraflag) { if (lineflag && line[j] >= 0) drawflag = 0; if (triflag && tri[j] >= 0) drawflag = 0; + if (ellipsoidflag && ellipsoid[j] >= 0) drawflag = 0; if (bodyflag && body[j] >= 0) drawflag = 0; } @@ -1403,13 +1219,13 @@ void DumpImage::create_image() } double opacity = aopacity[atom->type[j]]; - MathExtra::quat_to_mat(avec_tri->bonus[tri[i]].quat,mat); - MathExtra::matvec(mat,avec_tri->bonus[tri[i]].c1,pt1); - MathExtra::matvec(mat,avec_tri->bonus[tri[i]].c2,pt2); - MathExtra::matvec(mat,avec_tri->bonus[tri[i]].c3,pt3); - MathExtra::add3(pt1,x[i],pt1); - MathExtra::add3(pt2,x[i],pt2); - MathExtra::add3(pt3,x[i],pt3); + MathExtra::quat_to_mat(avec_tri->bonus[tri[j]].quat,mat); + MathExtra::matvec(mat,avec_tri->bonus[tri[j]].c1,pt1); + MathExtra::matvec(mat,avec_tri->bonus[tri[j]].c2,pt2); + MathExtra::matvec(mat,avec_tri->bonus[tri[j]].c3,pt3); + MathExtra::add3(pt1,x[j],pt1); + MathExtra::add3(pt2,x[j],pt2); + MathExtra::add3(pt3,x[j],pt3); if (tridraw) image->draw_triangle(pt1,pt2,pt3,color,opacity); if (edgedraw) { @@ -1420,6 +1236,26 @@ void DumpImage::create_image() } } + // render atoms that are ellipsoids + // estyle = 1 for tri only, estyle 2 for wireframe only, estyle 3 for both + + if (ellipsoidflag) { + double **x = atom->x; + int *ellipsoid = atom->ellipsoid; + int *type = atom->type; + for (i = 0; i < nchoose; i++) { + j = clist[i]; + if (ellipsoid[j] < 0) continue; + + if (ecolor == TYPE) { + color = colortype[type[j]]; + } + EllipsoidObj e(elevel); + e.draw(image, estyle, color, x[j], avec_ellipsoid->bonus[ellipsoid[j]].shape, + avec_ellipsoid->bonus[ellipsoid[j]].quat, ediamvalue, aopacity[type[j]]); + } + } + // render atoms that are bodies if (bodyflag) { @@ -1434,16 +1270,23 @@ void DumpImage::create_image() if (bodycolor == TYPE) { itype = static_cast(buf[m]); color = colortype[itype]; + } else if (bodycolor == INDEX) { + itype = (body[j] % atom->ntypes) + 1; + color = colortype[itype]; + } else { + color = image->color2rgb("white"); } double opacity = aopacity[atom->type[j]]; ibonus = body[j]; n = bptr->image(ibonus,bodyflag1,bodyflag2,bodyvec,bodyarray); for (k = 0; k < n; k++) { - if (bodyvec[k] == SPHERE) + if (bodyvec[k] == Graphics::SPHERE) image->draw_sphere(bodyarray[k],color,bodyarray[k][3],opacity); - else if (bodyvec[k] == LINE) + else if (bodyvec[k] == Graphics::LINE) image->draw_cylinder(&bodyarray[k][0],&bodyarray[k][3],color,bodyarray[k][6],3,opacity); + else if (bodyvec[k] == Graphics::TRI) + image->draw_triangle(&bodyarray[k][0],&bodyarray[k][3],&bodyarray[k][6],color,opacity); } m += size_one; @@ -1709,65 +1552,93 @@ void DumpImage::create_image() } } + // clang-format on // render objects provided by fixes for (const auto &ifix : fixes) { int *fixvec = nullptr; double **fixarray = nullptr; const int ntypes = atom->ntypes; - n = ifix.ptr->image(fixvec,fixarray); + n = ifix.ptr->image(fixvec, fixarray); for (i = 0; i < n; i++) { if (!fixvec || !fixarray) continue; - // set color + // set color and transparency + double opacity = ifix.opacity; if (ifix.colorstyle == TYPE) { itype = static_cast(fixarray[i][0] - 1.0) % ntypes + 1; color = colortype[itype]; - } else if (ifix.colorstyle == ELEMENT) { + } else if (ifix.colorstyle == ELEMENT) { itype = static_cast(fixarray[i][0] - 1.0) % ntypes + 1; color = colorelement[itype]; - } else if (ifix.colorstyle == CONSTANT) { + } else if (ifix.colorstyle == CONSTANT) { color = ifix.rgb; } else { - color = image->color2rgb("red"); + color = image->color2rgb("white"); + opacity = 1.0; } - if (fixvec[i] == SPHERE) { - image->draw_sphere(&fixarray[i][1],color,fixarray[i][4]+ifix.flag2); - } else if (fixvec[i] == LINE) { + if (fixvec[i] == Graphics::SPHERE) { + diameter = fixarray[i][4]; + if (fixarray[i][4] < 0) { + if (adiam == NUMERIC) { + diameter = adiamvalue; + } else if (adiam == TYPE) { + diameter = diamtype[itype]; + } else if (adiam == ELEMENT) { + diameter = diamelement[itype]; + } + } + image->draw_sphere(&fixarray[i][1], color, diameter + ifix.flag2, opacity); + } else if (fixvec[i] == Graphics::LINE) { // @sjplimp for consistency this should be: // image->draw_cylinder(&fixarray[i][1],&fixarray[i][4],color,ifix.flag2,ifix.flag1); - image->draw_cylinder(&fixarray[i][1],&fixarray[i][4],color,ifix.flag1,3); - } else if (fixvec[i] == TRI) { // don't render surface meshes in 2d + image->draw_cylinder(&fixarray[i][1], &fixarray[i][4], color, ifix.flag1, 3, opacity); + } else if (fixvec[i] == Graphics::TRI) { // don't render surface meshes in 2d if (domain->dimension == 3) { p1 = &fixarray[i][1]; p2 = &fixarray[i][4]; p3 = &fixarray[i][7]; if (static_cast(ifix.flag1) % 2) { - image->draw_triangle(p1,p2,p3,color,ifix.flag2); + image->draw_triangle(p1, p2, p3, color, opacity); } else { - image->draw_cylinder(p1,p2,color,ifix.flag2,3); - image->draw_cylinder(p2,p3,color,ifix.flag2,3); - image->draw_cylinder(p3,p1,color,ifix.flag2,3); + image->draw_cylinder(p1, p2, color, ifix.flag2, 3, opacity); + image->draw_cylinder(p2, p3, color, ifix.flag2, 3, opacity); + image->draw_cylinder(p3, p1, color, ifix.flag2, 3, opacity); } } - } else if (fixvec[i] == CYLINDER) { - image->draw_cylinder(&fixarray[i][1],&fixarray[i][4],color, - fixarray[i][7]+ifix.flag2,(int)ifix.flag1); - } else if (fixvec[i] == TRIANGLE) { - image->draw_triangle(&fixarray[i][1],&fixarray[i][4],&fixarray[i][7],color,ifix.flag2); - } else if (fixvec[i] == BOND) { + } else if (fixvec[i] == Graphics::CYLINDER) { + image->draw_cylinder(&fixarray[i][1], &fixarray[i][4], color, fixarray[i][7] + ifix.flag2, + (int) ifix.flag1, opacity); + } else if (fixvec[i] == Graphics::TRIANGLE) { + image->draw_triangle(&fixarray[i][1], &fixarray[i][4], &fixarray[i][7], color, opacity); + } else if (fixvec[i] == Graphics::ARROW) { + ArrowObj a(fixarray[i][9]); + a.draw(image, color, &fixarray[i][1], fixarray[i][7], &fixarray[i][4], fixarray[i][8], + opacity); + } else if (fixvec[i] == Graphics::CONE) { + ConeObj c(1.0, fixarray[i][7] + ifix.flag2, fixarray[i][8] + ifix.flag2, + (int) fixarray[i][9]); + c.draw(image, vec3{fixarray[i][1], fixarray[i][2], fixarray[i][3]}, + vec3{fixarray[i][4], fixarray[i][5], fixarray[i][6]}, color, opacity); + } else if (fixvec[i] == Graphics::PIXMAP) { + // get pointer to pixmap buffer and get background transparency color + const auto *pixmap = (const unsigned char *) ubuf(fixarray[i][6]).i; + double transcolor[3] = {fixarray[i][7], fixarray[i][8], fixarray[i][9]}; + image->draw_pixmap(&fixarray[i][1], (int) fixarray[i][4], (int) fixarray[i][5], pixmap, + transcolor, fixarray[i][10], opacity); + } else if (fixvec[i] == Graphics::BOND) { int type1 = static_cast(fixarray[i][0] - 1.0) % ntypes + 1; int type2 = static_cast(fixarray[i][1] - 1.0) % ntypes + 1; - double *color1; - double *color2; + double *color1, *color2; + double opacity = ifix.opacity; if (ifix.colorstyle == TYPE) { color1 = colortype[type1]; color2 = colortype[type2]; } else if (ifix.colorstyle == ELEMENT) { color1 = colorelement[type1]; color2 = colorelement[type2]; - } else if (ifix.colorstyle == CONSTANT) { + } else if (ifix.colorstyle == CONSTANT) { color1 = ifix.rgb; color2 = ifix.rgb; } else { @@ -1780,11 +1651,11 @@ void DumpImage::create_image() if (adiam == NUMERIC) { diameter = adiamvalue; } else if (adiam == TYPE) { - diameter = MIN(diamtype[type1],diamtype[type2]); + diameter = MIN(diamtype[type1], diamtype[type2]); } else if (adiam == ELEMENT) { - diameter = MIN(diamelement[type1],diamelement[type2]); + diameter = MIN(diamelement[type1], diamelement[type2]); } else if (adiam == ATTRIBUTE) { - diameter = MIN(bufcopy[atom1][1],bufcopy[atom2][1]); + diameter = MIN(bufcopy[atom1][1], bufcopy[atom2][1]); } } else { diameter = bdiamvalue; @@ -1794,21 +1665,21 @@ void DumpImage::create_image() // draw bond cylinder in 2 pieces - int capflag = ifix.flag1 ? 3 : 0; + int capflag = (ifix.flag1 != 0.0) ? 3 : 0; delx = fixarray[i][5] - fixarray[i][2]; dely = fixarray[i][6] - fixarray[i][3]; delz = fixarray[i][7] - fixarray[i][4]; - domain->minimum_image(FLERR,delx,dely,delz); + domain->minimum_image(FLERR, delx, dely, delz); double xmid[3]; - xmid[0] = fixarray[i][2] + 0.5*delx; - xmid[1] = fixarray[i][3] + 0.5*dely; - xmid[2] = fixarray[i][4] + 0.5*delz; - image->draw_cylinder(&fixarray[i][2],xmid,color1,diameter,capflag); - xmid[0] = fixarray[i][5] - 0.5*delx; - xmid[1] = fixarray[i][6] - 0.5*dely; - xmid[2] = fixarray[i][7] - 0.5*delz; - image->draw_cylinder(xmid,&fixarray[i][5],color2,diameter,capflag); + xmid[0] = fixarray[i][2] + 0.5 * delx; + xmid[1] = fixarray[i][3] + 0.5 * dely; + xmid[2] = fixarray[i][4] + 0.5 * delz; + image->draw_cylinder(&fixarray[i][2], xmid, color1, diameter, capflag, opacity); + xmid[0] = fixarray[i][5] - 0.5 * delx; + xmid[1] = fixarray[i][6] - 0.5 * dely; + xmid[2] = fixarray[i][7] - 0.5 * delz; + image->draw_cylinder(xmid, &fixarray[i][5], color2, diameter, capflag, opacity); } } } @@ -1829,7 +1700,7 @@ void DumpImage::create_image() // for POINTS style we have the same code for all region styles if (reg.style == POINTS) { - int seed = (int)(platform::walltime()*1000000) % 1000000; + int seed = (int) (platform::walltime() * 1000000) % 1000000; RanMars rand(lmp, seed); double pos[3]; @@ -1848,382 +1719,196 @@ void DumpImage::create_image() zlen = domain->boxhi_bound[2] - domain->boxlo_bound[2]; } + // temporarily turn off the region's open faces flag since + // otherwise Region::match() will match for *any* position + int saved_flag = reg.ptr->openflag; + reg.ptr->openflag = 0; + for (int i = 0; i < reg.npoints; ++i) { pos[0] = rand.uniform() * xlen + xoff; pos[1] = rand.uniform() * ylen + yoff; pos[2] = rand.uniform() * zlen + zoff; - if (reg.ptr->inside(pos[0], pos[1], pos[2])) { - reg.ptr->forward_transform(pos[0], pos[1], pos[2]); + if (reg.ptr->match(pos[0], pos[1], pos[2])) image->draw_sphere(pos, reg.color, reg.diameter); - } } + // restore saved open face flag + reg.ptr->openflag = saved_flag; + } else { std::string regstyle = reg.ptr->style; - if (regstyle == "block") { - auto *myreg = dynamic_cast(reg.ptr); - // inconsistent style. should not happen. - if (!myreg) continue; + // prism differs from block only in the positions of the corners; handle them together + if ((regstyle == "block") || (regstyle == "prism")) { + + // set the positions of the corners + using cornerdata = std::array; + cornerdata corners; + + if (regstyle == "block") { + auto *myreg = dynamic_cast(reg.ptr); + // inconsistent style. should not happen. + if (!myreg) continue; + + corners = cornerdata{ + vec3{myreg->xlo, myreg->ylo, myreg->zlo}, vec3{myreg->xlo, myreg->ylo, myreg->zhi}, + vec3{myreg->xlo, myreg->yhi, myreg->zhi}, vec3{myreg->xlo, myreg->yhi, myreg->zlo}, + vec3{myreg->xhi, myreg->ylo, myreg->zlo}, vec3{myreg->xhi, myreg->ylo, myreg->zhi}, + vec3{myreg->xhi, myreg->yhi, myreg->zhi}, vec3{myreg->xhi, myreg->yhi, myreg->zlo}}; + } + + if (regstyle == "prism") { + auto *myreg = dynamic_cast(reg.ptr); + // inconsistent style. should not happen. + if (!myreg) continue; + + corners = cornerdata{ + vec3{myreg->xlo, myreg->ylo, myreg->zlo}, + vec3{myreg->xlo + myreg->xz, myreg->ylo + myreg->yz, myreg->zhi}, + vec3{myreg->xlo + myreg->xy + myreg->xz, myreg->yhi + myreg->yz, myreg->zhi}, + vec3{myreg->xlo + myreg->xy, myreg->yhi, myreg->zlo}, + vec3{myreg->xhi, myreg->ylo, myreg->zlo}, + vec3{myreg->xhi + myreg->xz, myreg->ylo + myreg->yz, myreg->zhi}, + vec3{myreg->xhi + myreg->xy + myreg->xz, myreg->yhi + myreg->yz, myreg->zhi}, + vec3{myreg->xhi + myreg->xy, myreg->yhi, myreg->zlo}}; + } - double block[8][3]; - block[0][0] = myreg->xlo; block[0][1] = myreg->ylo; block[0][2] = myreg->zlo; - block[1][0] = myreg->xlo; block[1][1] = myreg->ylo; block[1][2] = myreg->zhi; - block[2][0] = myreg->xlo; block[2][1] = myreg->yhi; block[2][2] = myreg->zhi; - block[3][0] = myreg->xlo; block[3][1] = myreg->yhi; block[3][2] = myreg->zlo; - block[4][0] = myreg->xhi; block[4][1] = myreg->ylo; block[4][2] = myreg->zlo; - block[5][0] = myreg->xhi; block[5][1] = myreg->ylo; block[5][2] = myreg->zhi; - block[6][0] = myreg->xhi; block[6][1] = myreg->yhi; block[6][2] = myreg->zhi; - block[7][0] = myreg->xhi; block[7][1] = myreg->yhi; block[7][2] = myreg->zlo; for (int i = 0; i < 8; ++i) - reg.ptr->forward_transform(block[i][0], block[i][1], block[i][2]); + reg.ptr->forward_transform(corners[i][0], corners[i][1], corners[i][2]); + +#define DRAW_CYLINDER(i, j) \ + image->draw_cylinder(corners[i].data(), corners[j].data(), reg.color, reg.diameter, 3, 1.0) +#define DRAW_TRIANGLE(i, j, k) \ + image->draw_triangle(corners[i].data(), corners[j].data(), corners[k].data(), reg.color, opacity) if (reg.style == FRAME) { - image->draw_cylinder(block[0],block[1],reg.color,reg.diameter,3); - image->draw_cylinder(block[1],block[2],reg.color,reg.diameter,3); - image->draw_cylinder(block[0],block[3],reg.color,reg.diameter,3); - image->draw_cylinder(block[2],block[3],reg.color,reg.diameter,3); - image->draw_cylinder(block[0],block[4],reg.color,reg.diameter,3); - image->draw_cylinder(block[1],block[5],reg.color,reg.diameter,3); - image->draw_cylinder(block[2],block[6],reg.color,reg.diameter,3); - image->draw_cylinder(block[3],block[7],reg.color,reg.diameter,3); - image->draw_cylinder(block[4],block[5],reg.color,reg.diameter,3); - image->draw_cylinder(block[5],block[6],reg.color,reg.diameter,3); - image->draw_cylinder(block[4],block[7],reg.color,reg.diameter,3); - image->draw_cylinder(block[6],block[7],reg.color,reg.diameter,3); + DRAW_CYLINDER(0, 1); + DRAW_CYLINDER(1, 2); + DRAW_CYLINDER(0, 3); + DRAW_CYLINDER(2, 3); + DRAW_CYLINDER(0, 4); + DRAW_CYLINDER(1, 5); + DRAW_CYLINDER(2, 6); + DRAW_CYLINDER(3, 7); + DRAW_CYLINDER(4, 5); + DRAW_CYLINDER(5, 6); + DRAW_CYLINDER(4, 7); + DRAW_CYLINDER(6, 7); } else if ((reg.style == FILLED) || (reg.style == TRANSPARENT)) { double opacity = (reg.style == TRANSPARENT) ? reg.opacity : 1.0; - if (!myreg->open_faces[0]) { - image->draw_triangle(block[0], block[1], block[2], reg.color, opacity); - image->draw_triangle(block[2], block[3], block[0], reg.color, opacity); + if (!reg.ptr->open_faces[0]) { + DRAW_TRIANGLE(0, 1, 2); + DRAW_TRIANGLE(2, 3, 0); } - if (!myreg->open_faces[1]) { - image->draw_triangle(block[4], block[5], block[6], reg.color, opacity); - image->draw_triangle(block[6], block[7], block[4], reg.color, opacity); + if (!reg.ptr->open_faces[1]) { + DRAW_TRIANGLE(4, 5, 6); + DRAW_TRIANGLE(6, 7, 4); } - if (!myreg->open_faces[2]) { - image->draw_triangle(block[0], block[4], block[7], reg.color, opacity); - image->draw_triangle(block[7], block[3], block[0], reg.color, opacity); + if (!reg.ptr->open_faces[2]) { + DRAW_TRIANGLE(0, 4, 7); + DRAW_TRIANGLE(7, 3, 0); } - if (!myreg->open_faces[3]) { - image->draw_triangle(block[1], block[2], block[6], reg.color, opacity); - image->draw_triangle(block[6], block[5], block[1], reg.color, opacity); + if (!reg.ptr->open_faces[3]) { + DRAW_TRIANGLE(1, 2, 6); + DRAW_TRIANGLE(6, 5, 1); } - if (!myreg->open_faces[4]) { - image->draw_triangle(block[0], block[1], block[5], reg.color, opacity); - image->draw_triangle(block[5], block[4], block[0], reg.color, opacity); + if (!reg.ptr->open_faces[4]) { + DRAW_TRIANGLE(0, 1, 5); + DRAW_TRIANGLE(5, 4, 0); } - if (!myreg->open_faces[5]) { - image->draw_triangle(block[3], block[2], block[6], reg.color, opacity); - image->draw_triangle(block[6], block[7], block[3], reg.color, opacity); + if (!reg.ptr->open_faces[5]) { + DRAW_TRIANGLE(3, 2, 6); + DRAW_TRIANGLE(6, 7, 3); } } - - } else if (regstyle == "cone") { - auto *myreg = dynamic_cast(reg.ptr); - // inconsistent style. should not happen. - if (!myreg) continue; - - // construct coordinates for lo/hi tip/center of cone - double lo[3], hi[3]; - if (myreg->axis == 'x') { - lo[0] = myreg->lo; - lo[1] = myreg->c1; - lo[2] = myreg->c2; - hi[0] = myreg->hi; - hi[1] = myreg->c1; - hi[2] = myreg->c2; - } else if (myreg->axis == 'y') { - lo[0] = myreg->c1; - lo[1] = myreg->lo; - lo[2] = myreg->c2; - hi[0] = myreg->c1; - hi[1] = myreg->hi; - hi[2] = myreg->c2; - } else { // myreg->axis == 'z' - lo[0] = myreg->c1; - lo[1] = myreg->c2; - lo[2] = myreg->lo; - hi[0] = myreg->c1; - hi[1] = myreg->c2; - hi[2] = myreg->hi; +#undef DRAW_CYLINDER +#undef DRAW_TRIANGLE + + // cylinder is just a special case of cone so we can handle them together + } else if ((regstyle == "cone") || (regstyle == "cylinder")) { + + // construct triangle mesh and store direction and hi/lo center coordinates + vec3 lo, hi; + double length = 0.0, radiuslo = 0.0, radiushi = 0.0; + double xdir = 0.0, ydir = 0.0, zdir = 0.0; + + if (regstyle == "cone") { + auto *myreg = dynamic_cast(reg.ptr); + // inconsistent style. should not happen. + if (!myreg) continue; + + length = myreg->hi - myreg->lo; + radiuslo = myreg->radiuslo; + radiushi = myreg->radiushi; + if (myreg->axis == 'x') { + xdir = 1.0; + lo = {myreg->lo, myreg->c1, myreg->c2}; + hi = {myreg->hi, myreg->c1, myreg->c2}; + } else if (myreg->axis == 'y') { + ydir = 1.0; + lo = {myreg->c1, myreg->lo, myreg->c2}; + hi = {myreg->c1, myreg->hi, myreg->c2}; + } else { // myreg->axis == 'z' + zdir = 1.0; + lo = {myreg->c1, myreg->c2, myreg->lo}; + hi = {myreg->c1, myreg->c2, myreg->hi}; + } } - // Apply forward_transform for cone lo/hi tip/center for dynamic regions - myreg->forward_transform(lo[0], lo[1], lo[2]); - myreg->forward_transform(hi[0], hi[1], hi[2]); - double p1[3], p2[3], p3[3], p4[3]; - if (reg.style == FRAME) { - for (int i = 0; i < RESOLUTION; ++i) { - if (myreg->axis == 'x') { - p1[0] = p2[0] = myreg->lo; - p3[0] = p4[0] = myreg->hi; - p1[1] = myreg->radiuslo * sin(RADINC * i) + myreg->c1; - p1[2] = myreg->radiuslo * cos(RADINC * i) + myreg->c2; - p2[1] = myreg->radiuslo * sin(RADINC * (i+1)) + myreg->c1; - p2[2] = myreg->radiuslo * cos(RADINC * (i+1)) + myreg->c2; - p3[1] = myreg->radiushi * sin(RADINC * i) + myreg->c1; - p3[2] = myreg->radiushi * cos(RADINC * i) + myreg->c2; - p4[1] = myreg->radiushi * sin(RADINC * (i+1)) + myreg->c1; - p4[2] = myreg->radiushi * cos(RADINC * (i+1)) + myreg->c2; - myreg->forward_transform(p1[0], p1[1], p1[2]); - myreg->forward_transform(p2[0], p2[1], p2[2]); - myreg->forward_transform(p3[0], p3[1], p3[2]); - myreg->forward_transform(p4[0], p4[1], p4[2]); - image->draw_cylinder(p1, p2, reg.color, reg.diameter, 3); - image->draw_cylinder(p3, p4, reg.color, reg.diameter, 3); - image->draw_cylinder(p1, p3, reg.color, reg.diameter, 3); - image->draw_cylinder(p2, p4, reg.color, reg.diameter, 3); - } else if (myreg->axis == 'y') { - p1[1] = p2[1] = myreg->lo; - p3[1] = p4[1] = myreg->hi; - p1[0] = myreg->radiuslo * sin(RADINC * i) + myreg->c1; - p1[2] = myreg->radiuslo * cos(RADINC * i) + myreg->c2; - p2[0] = myreg->radiuslo * sin(RADINC * (i+1)) + myreg->c1; - p2[2] = myreg->radiuslo * cos(RADINC * (i+1)) + myreg->c2; - p3[0] = myreg->radiushi * sin(RADINC * i) + myreg->c1; - p3[2] = myreg->radiushi * cos(RADINC * i) + myreg->c2; - p4[0] = myreg->radiushi * sin(RADINC * (i+1)) + myreg->c1; - p4[2] = myreg->radiushi * cos(RADINC * (i+1)) + myreg->c2; - myreg->forward_transform(p1[0], p1[1], p1[2]); - myreg->forward_transform(p2[0], p2[1], p2[2]); - myreg->forward_transform(p3[0], p3[1], p3[2]); - myreg->forward_transform(p4[0], p4[1], p4[2]); - image->draw_cylinder(p1, p2, reg.color, reg.diameter, 3); - image->draw_cylinder(p3, p4, reg.color, reg.diameter, 3); - image->draw_cylinder(p1, p3, reg.color, reg.diameter, 3); - image->draw_cylinder(p2, p4, reg.color, reg.diameter, 3); - } else { // if (myreg->axis == 'z') - p1[2] = p2[2] = myreg->lo; - p3[2] = p4[2] = myreg->hi; - p1[0] = myreg->radiuslo * sin(RADINC * i) + myreg->c1; - p1[1] = myreg->radiuslo * cos(RADINC * i) + myreg->c2; - p2[0] = myreg->radiuslo * sin(RADINC * (i+1)) + myreg->c1; - p2[1] = myreg->radiuslo * cos(RADINC * (i+1)) + myreg->c2; - p3[0] = myreg->radiushi * sin(RADINC * i) + myreg->c1; - p3[1] = myreg->radiushi * cos(RADINC * i) + myreg->c2; - p4[0] = myreg->radiushi * sin(RADINC * (i+1)) + myreg->c1; - p4[1] = myreg->radiushi * cos(RADINC * (i+1)) + myreg->c2; - myreg->forward_transform(p1[0], p1[1], p1[2]); - myreg->forward_transform(p2[0], p2[1], p2[2]); - myreg->forward_transform(p3[0], p3[1], p3[2]); - myreg->forward_transform(p4[0], p4[1], p4[2]); - image->draw_cylinder(p1, p2, reg.color, reg.diameter, 3); - image->draw_cylinder(p3, p4, reg.color, reg.diameter, 3); - image->draw_cylinder(p1, p3, reg.color, reg.diameter, 3); - image->draw_cylinder(p2, p4, reg.color, reg.diameter, 3); - } - } - } else if ((reg.style == FILLED) || (reg.style == TRANSPARENT)) { - double opacity = (reg.style == TRANSPARENT) ? reg.opacity : 1.0; - for (int i = 0; i < RESOLUTION; ++i) { - if (myreg->axis == 'x') { - p1[0] = p2[0] = myreg->lo; - p3[0] = p4[0] = myreg->hi; - p1[1] = myreg->radiuslo * sin(RADINC * i) + myreg->c1; - p1[2] = myreg->radiuslo * cos(RADINC * i) + myreg->c2; - p2[1] = myreg->radiuslo * sin(RADINC * (i+1)) + myreg->c1; - p2[2] = myreg->radiuslo * cos(RADINC * (i+1)) + myreg->c2; - p3[1] = myreg->radiushi * sin(RADINC * i) + myreg->c1; - p3[2] = myreg->radiushi * cos(RADINC * i) + myreg->c2; - p4[1] = myreg->radiushi * sin(RADINC * (i+1)) + myreg->c1; - p4[2] = myreg->radiushi * cos(RADINC * (i+1)) + myreg->c2; - myreg->forward_transform(p1[0], p1[1], p1[2]); - myreg->forward_transform(p2[0], p2[1], p2[2]); - myreg->forward_transform(p3[0], p3[1], p3[2]); - myreg->forward_transform(p4[0], p4[1], p4[2]); - if (!myreg->open_faces[0]) image->draw_triangle(p1, p2, lo, reg.color, opacity); - if (!myreg->open_faces[1]) image->draw_triangle(p3, p4, hi, reg.color, opacity); - if (!myreg->open_faces[2]) { - image->draw_triangle(p1, p2, p3, reg.color, opacity); - image->draw_triangle(p2, p4, p3, reg.color, opacity); - } - - } else if (myreg->axis == 'y') { - p1[1] = p2[1] = myreg->lo; - p3[1] = p4[1] = myreg->hi; - p1[0] = myreg->radiuslo * sin(RADINC * i) + myreg->c1; - p1[2] = myreg->radiuslo * cos(RADINC * i) + myreg->c2; - p2[0] = myreg->radiuslo * sin(RADINC * (i+1)) + myreg->c1; - p2[2] = myreg->radiuslo * cos(RADINC * (i+1)) + myreg->c2; - p3[0] = myreg->radiushi * sin(RADINC * i) + myreg->c1; - p3[2] = myreg->radiushi * cos(RADINC * i) + myreg->c2; - p4[0] = myreg->radiushi * sin(RADINC * (i+1)) + myreg->c1; - p4[2] = myreg->radiushi * cos(RADINC * (i+1)) + myreg->c2; - myreg->forward_transform(p1[0], p1[1], p1[2]); - myreg->forward_transform(p2[0], p2[1], p2[2]); - myreg->forward_transform(p3[0], p3[1], p3[2]); - myreg->forward_transform(p4[0], p4[1], p4[2]); - if (!myreg->open_faces[0]) image->draw_triangle(p1, p2, lo, reg.color, opacity); - if (!myreg->open_faces[1]) image->draw_triangle(p3, p4, hi, reg.color, opacity); - if (!myreg->open_faces[2]) { - image->draw_triangle(p1, p2, p3, reg.color, opacity); - image->draw_triangle(p2, p4, p3, reg.color, opacity); - } - } else { // if (myreg->axis == 'z') - p1[2] = p2[2] = myreg->lo; - p3[2] = p4[2] = myreg->hi; - p1[0] = myreg->radiuslo * sin(RADINC * i) + myreg->c1; - p1[1] = myreg->radiuslo * cos(RADINC * i) + myreg->c2; - p2[0] = myreg->radiuslo * sin(RADINC * (i+1)) + myreg->c1; - p2[1] = myreg->radiuslo * cos(RADINC * (i+1)) + myreg->c2; - p3[0] = myreg->radiushi * sin(RADINC * i) + myreg->c1; - p3[1] = myreg->radiushi * cos(RADINC * i) + myreg->c2; - p4[0] = myreg->radiushi * sin(RADINC * (i+1)) + myreg->c1; - p4[1] = myreg->radiushi * cos(RADINC * (i+1)) + myreg->c2; - myreg->forward_transform(p1[0], p1[1], p1[2]); - myreg->forward_transform(p2[0], p2[1], p2[2]); - myreg->forward_transform(p3[0], p3[1], p3[2]); - myreg->forward_transform(p4[0], p4[1], p4[2]); - if (!myreg->open_faces[0]) image->draw_triangle(p1, p2, lo, reg.color, opacity); - if (!myreg->open_faces[1]) image->draw_triangle(p3, p4, hi, reg.color, opacity); - if (!myreg->open_faces[2]) { - image->draw_triangle(p1, p2, p3, reg.color, opacity); - image->draw_triangle(p2, p4, p3, reg.color, opacity); - } - } + double radius = 0.0; + if (regstyle == "cylinder") { + auto *myreg = dynamic_cast(reg.ptr); + // inconsistent style. should not happen. + if (!myreg) continue; + + length = myreg->hi - myreg->lo; + radiuslo = myreg->radius; + radiushi = myreg->radius; + if (myreg->axis == 'x') { + xdir = 1.0; + lo = {myreg->lo, myreg->c1, myreg->c2}; + hi = {myreg->hi, myreg->c1, myreg->c2}; + } else if (myreg->axis == 'y') { + ydir = 1.0; + lo = {myreg->c1, myreg->lo, myreg->c2}; + hi = {myreg->c1, myreg->hi, myreg->c2}; + } else { // myreg->axis == 'z' + zdir = 1.0; + lo = {myreg->c1, myreg->c2, myreg->lo}; + hi = {myreg->c1, myreg->c2, myreg->hi}; } } - } else if (regstyle == "cylinder") { - auto *myreg = dynamic_cast(reg.ptr); - // inconsistent style. should not happen. - if (!myreg) continue; + // compute direction vector and positions of lo/hi centers - // construct coordinates for lo/hi center of cylinder - double lo[3], hi[3]; - if (myreg->axis == 'x') { - lo[0] = myreg->lo; - lo[1] = myreg->c1; - lo[2] = myreg->c2; - hi[0] = myreg->hi; - hi[1] = myreg->c1; - hi[2] = myreg->c2; - } else if (myreg->axis == 'y') { - lo[0] = myreg->c1; - lo[1] = myreg->lo; - lo[2] = myreg->c2; - hi[0] = myreg->c1; - hi[1] = myreg->hi; - hi[2] = myreg->c2; - } else { // myreg->axis == 'z' - lo[0] = myreg->c1; - lo[1] = myreg->c2; - lo[2] = myreg->lo; - hi[0] = myreg->c1; - hi[1] = myreg->c2; - hi[2] = myreg->hi; - } - // Apply forward_transform for cylinder lo/hi center for dynamic regions - myreg->forward_transform(lo[0], lo[1], lo[2]); - myreg->forward_transform(hi[0], hi[1], hi[2]); + vec3 dir{xdir, ydir, zdir}; + vec3 mid = 0.5 * (hi + lo); - double p1[3], p2[3], p3[3], p4[3]; + // determine which faces to draw + int faceflag = 0; if (reg.style == FRAME) { - for (int i = 0; i < RESOLUTION; ++i) { - if (myreg->axis == 'x') { - p1[0] = p2[0] = myreg->lo; - p3[0] = p4[0] = myreg->hi; - p1[1] = p3[1] = myreg->radius * sin(RADINC * i) + myreg->c1; - p1[2] = p3[2] = myreg->radius * cos(RADINC * i) + myreg->c2; - p2[1] = p4[1] = myreg->radius * sin(RADINC * (i+1)) + myreg->c1; - p2[2] = p4[2] = myreg->radius * cos(RADINC * (i+1)) + myreg->c2; - myreg->forward_transform(p1[0], p1[1], p1[2]); - myreg->forward_transform(p2[0], p2[1], p2[2]); - myreg->forward_transform(p3[0], p3[1], p3[2]); - myreg->forward_transform(p4[0], p4[1], p4[2]); - image->draw_cylinder(p1, p2, reg.color, reg.diameter, 3); - image->draw_cylinder(p3, p4, reg.color, reg.diameter, 3); - image->draw_cylinder(p1, p3, reg.color, reg.diameter, 3); - image->draw_cylinder(p2, p4, reg.color, reg.diameter, 3); - } else if (myreg->axis == 'y') { - p1[1] = p2[1] = myreg->lo; - p3[1] = p4[1] = myreg->hi; - p1[0] = p3[0] = myreg->radius * sin(RADINC * i) + myreg->c1; - p1[2] = p3[2] = myreg->radius * cos(RADINC * i) + myreg->c2; - p2[0] = p4[0] = myreg->radius * sin(RADINC * (i+1)) + myreg->c1; - p2[2] = p4[2] = myreg->radius * cos(RADINC * (i+1)) + myreg->c2; - myreg->forward_transform(p1[0], p1[1], p1[2]); - myreg->forward_transform(p2[0], p2[1], p2[2]); - myreg->forward_transform(p3[0], p3[1], p3[2]); - myreg->forward_transform(p4[0], p4[1], p4[2]); - image->draw_cylinder(p1, p2, reg.color, reg.diameter, 3); - image->draw_cylinder(p3, p4, reg.color, reg.diameter, 3); - image->draw_cylinder(p1, p3, reg.color, reg.diameter, 3); - image->draw_cylinder(p2, p4, reg.color, reg.diameter, 3); - } else { // if (myreg->axis == 'z') - p1[2] = p2[2] = myreg->lo; - p3[2] = p4[2] = myreg->hi; - p1[0] = p3[0] = myreg->radius * sin(RADINC * i) + myreg->c1; - p1[1] = p3[1] = myreg->radius * cos(RADINC * i) + myreg->c2; - p2[0] = p4[0] = myreg->radius * sin(RADINC * (i+1)) + myreg->c1; - p2[1] = p4[1] = myreg->radius * cos(RADINC * (i+1)) + myreg->c2; - myreg->forward_transform(p1[0], p1[1], p1[2]); - myreg->forward_transform(p2[0], p2[1], p2[2]); - myreg->forward_transform(p3[0], p3[1], p3[2]); - myreg->forward_transform(p4[0], p4[1], p4[2]); - image->draw_cylinder(p1, p2, reg.color, reg.diameter, 3); - image->draw_cylinder(p3, p4, reg.color, reg.diameter, 3); - image->draw_cylinder(p1, p3, reg.color, reg.diameter, 3); - image->draw_cylinder(p2, p4, reg.color, reg.diameter, 3); - } - } - } else if ((reg.style == FILLED) || (reg.style == TRANSPARENT)) { - double opacity = (reg.style == TRANSPARENT) ? reg.opacity : 1.0; - for (int i = 0; i < RESOLUTION; ++i) { - if (myreg->axis == 'x') { - p1[0] = p2[0] = myreg->lo; - p3[0] = p4[0] = myreg->hi; - p1[1] = p3[1] = myreg->radius * sin(RADINC * i) + myreg->c1; - p1[2] = p3[2] = myreg->radius * cos(RADINC * i) + myreg->c2; - p2[1] = p4[1] = myreg->radius * sin(RADINC * (i+1)) + myreg->c1; - p2[2] = p4[2] = myreg->radius * cos(RADINC * (i+1)) + myreg->c2; - myreg->forward_transform(p1[0], p1[1], p1[2]); - myreg->forward_transform(p2[0], p2[1], p2[2]); - myreg->forward_transform(p3[0], p3[1], p3[2]); - myreg->forward_transform(p4[0], p4[1], p4[2]); - if (!myreg->open_faces[0]) image->draw_triangle(p1, p2, lo, reg.color, opacity); - if (!myreg->open_faces[1]) image->draw_triangle(p3, p4, hi, reg.color, opacity); - if (!myreg->open_faces[2]) { - image->draw_triangle(p1, p2, p3, reg.color, opacity); - image->draw_triangle(p3, p4, p2, reg.color, opacity); - } - } else if (myreg->axis == 'y') { - p1[1] = p2[1] = myreg->lo; - p3[1] = p4[1] = myreg->hi; - p1[0] = p3[0] = myreg->radius * sin(RADINC * i) + myreg->c1; - p1[2] = p3[2] = myreg->radius * cos(RADINC * i) + myreg->c2; - p2[0] = p4[0] = myreg->radius * sin(RADINC * (i+1)) + myreg->c1; - p2[2] = p4[2] = myreg->radius * cos(RADINC * (i+1)) + myreg->c2; - myreg->forward_transform(p1[0], p1[1], p1[2]); - myreg->forward_transform(p2[0], p2[1], p2[2]); - myreg->forward_transform(p3[0], p3[1], p3[2]); - myreg->forward_transform(p4[0], p4[1], p4[2]); - if (!myreg->open_faces[0]) image->draw_triangle(p1, p2, lo, reg.color, opacity); - if (!myreg->open_faces[1]) image->draw_triangle(p3, p4, hi, reg.color, opacity); - if (!myreg->open_faces[2]) { - image->draw_triangle(p1, p2, p3, reg.color, opacity); - image->draw_triangle(p3, p4, p2, reg.color, opacity); - } - } else { // if (myreg->axis == 'z') - p1[2] = p2[2] = myreg->lo; - p3[2] = p4[2] = myreg->hi; - p1[0] = p3[0] = myreg->radius * sin(RADINC * i) + myreg->c1; - p1[1] = p3[1] = myreg->radius * cos(RADINC * i) + myreg->c2; - p2[0] = p4[0] = myreg->radius * sin(RADINC * (i+1)) + myreg->c1; - p2[1] = p4[1] = myreg->radius * cos(RADINC * (i+1)) + myreg->c2; - myreg->forward_transform(p1[0], p1[1], p1[2]); - myreg->forward_transform(p2[0], p2[1], p2[2]); - myreg->forward_transform(p3[0], p3[1], p3[2]); - myreg->forward_transform(p4[0], p4[1], p4[2]); - if (!myreg->open_faces[0]) image->draw_triangle(p1, p2, lo, reg.color, opacity); - if (!myreg->open_faces[1]) image->draw_triangle(p3, p4, hi, reg.color, opacity); - if (!myreg->open_faces[2]) { - image->draw_triangle(p1, p2, p3, reg.color, opacity); - image->draw_triangle(p2, p4, p3, reg.color, opacity); - } - } - } + faceflag = 4; // no caps + } else { + if (!reg.ptr->open_faces[0]) faceflag |= 1; + if (!reg.ptr->open_faces[1]) faceflag |= 2; + if (!reg.ptr->open_faces[2]) faceflag |= 4; + } + + // determine draw style flags + int drawflag = (reg.style == FRAME) ? 2 : 1; + double opacity = (reg.style == TRANSPARENT) ? reg.opacity : 1.0; + + ConeObj c(length, radiushi, radiuslo, faceflag, + (reg.style == FRAME) ? RESOLUTION : 2 * RESOLUTION); + c.draw(image, drawflag, dir, mid, reg.color, reg.ptr, reg.diameter, opacity); + + // draw an additional uncapped cylinder for a smoother image for filled cylinders only + if ((regstyle == "cylinder") && ((reg.style == FILLED) || (reg.style == TRANSPARENT))) { + + reg.ptr->forward_transform(lo[0], lo[1], lo[2]); + reg.ptr->forward_transform(hi[0], hi[1], hi[2]); + if (!reg.ptr->open_faces[2]) + image->draw_cylinder(lo.data(), hi.data(), reg.color, 2.0 * radius, 0, opacity); } } else if (regstyle == "ellipsoid") { @@ -2231,98 +1916,62 @@ void DumpImage::create_image() // inconsistent style. should not happen. if (!myreg) continue; - double center[3]; - center[0] = myreg->xc; - center[1] = myreg->yc; - center[2] = myreg->zc; + double center[3] = {myreg->xc, myreg->yc, myreg->zc}; double radius[3] = {myreg->a, myreg->b, myreg->c}; if (reg.style == FRAME) { - ellipsoid2wireframe(image, 4, reg.color, reg.diameter, center, radius, reg.ptr); + // we use a moderate mesh level for wireframe so we can better see what is inside + EllipsoidObj(4).draw(image, 2, reg.color, center, radius, reg.ptr, reg.diameter, 1.0); } else if ((reg.style == FILLED) || (reg.style == TRANSPARENT)) { - double opacity = (reg.style == TRANSPARENT) ? reg.opacity : 1.0; - ellipsoid2filled(image, 4, reg.color, center, radius, reg.ptr, opacity); + // we get a smoother surface with a one step higher mesh level (and 4x more triangles) + EllipsoidObj(5).draw(image, 1, reg.color, center, radius, reg.ptr, reg.diameter, + (reg.style == TRANSPARENT) ? reg.opacity : 1.0); } - } else if (regstyle == "prism") { - auto *myreg = dynamic_cast(reg.ptr); + } else if (regstyle == "plane") { + auto *myreg = dynamic_cast(reg.ptr); // inconsistent style. should not happen. if (!myreg) continue; - double block[8][3]; - block[0][0] = myreg->xlo; block[0][1] = myreg->ylo; block[0][2] = myreg->zlo; - block[1][0] = myreg->xlo + myreg->xz; block[1][1] = myreg->ylo + myreg->yz; block[1][2] = myreg->zhi; - block[2][0] = myreg->xlo + myreg->xy + myreg->xz; block[2][1] = myreg->yhi + myreg->yz; block[2][2] = myreg->zhi; - block[3][0] = myreg->xlo + myreg->xy; block[3][1] = myreg->yhi; block[3][2] = myreg->zlo; - block[4][0] = myreg->xhi; block[4][1] = myreg->ylo; block[4][2] = myreg->zlo; - block[5][0] = myreg->xhi + myreg->xz; block[5][1] = myreg->ylo + myreg->yz; block[5][2] = myreg->zhi; - block[6][0] = myreg->xhi + myreg->xy + myreg->xz; block[6][1] = myreg->yhi + myreg->yz; block[6][2] = myreg->zhi; - block[7][0] = myreg->xhi + myreg->xy; block[7][1] = myreg->yhi; block[7][2] = myreg->zlo; - for (int i = 0; i < 8; ++i) - reg.ptr->forward_transform(block[i][0], block[i][1], block[i][2]); - + // determine draw style flags + int flag = 1; + double opacity = 1.0; if (reg.style == FRAME) { - image->draw_cylinder(block[0],block[1],reg.color,reg.diameter,3); - image->draw_cylinder(block[1],block[2],reg.color,reg.diameter,3); - image->draw_cylinder(block[0],block[3],reg.color,reg.diameter,3); - image->draw_cylinder(block[2],block[3],reg.color,reg.diameter,3); - image->draw_cylinder(block[0],block[4],reg.color,reg.diameter,3); - image->draw_cylinder(block[1],block[5],reg.color,reg.diameter,3); - image->draw_cylinder(block[2],block[6],reg.color,reg.diameter,3); - image->draw_cylinder(block[3],block[7],reg.color,reg.diameter,3); - image->draw_cylinder(block[4],block[5],reg.color,reg.diameter,3); - image->draw_cylinder(block[5],block[6],reg.color,reg.diameter,3); - image->draw_cylinder(block[4],block[7],reg.color,reg.diameter,3); - image->draw_cylinder(block[6],block[7],reg.color,reg.diameter,3); - } else if ((reg.style == FILLED) || (reg.style == TRANSPARENT)) { - double opacity = (reg.style == TRANSPARENT) ? reg.opacity : 1.0; - if (!myreg->open_faces[0]) { - image->draw_triangle(block[0], block[1], block[2], reg.color, opacity); - image->draw_triangle(block[2], block[3], block[0], reg.color, opacity); - } - if (!myreg->open_faces[1]) { - image->draw_triangle(block[4], block[5], block[6], reg.color, opacity); - image->draw_triangle(block[6], block[7], block[4], reg.color, opacity); - } - if (!myreg->open_faces[2]) { - image->draw_triangle(block[0], block[4], block[7], reg.color, opacity); - image->draw_triangle(block[7], block[3], block[0], reg.color, opacity); - } - if (!myreg->open_faces[3]) { - image->draw_triangle(block[1], block[2], block[6], reg.color, opacity); - image->draw_triangle(block[6], block[5], block[1], reg.color, opacity); - } - if (!myreg->open_faces[4]) { - image->draw_triangle(block[0], block[1], block[5], reg.color, opacity); - image->draw_triangle(block[5], block[4], block[0], reg.color, opacity); - } - if (!myreg->open_faces[5]) { - image->draw_triangle(block[3], block[2], block[6], reg.color, opacity); - image->draw_triangle(block[6], block[7], block[3], reg.color, opacity); - } + flag = 2; + } else if (reg.style == TRANSPARENT) { + opacity = reg.opacity; } + double center[3] = {myreg->xp, myreg->yp, myreg->zp}; + double scale = MathExtra::len3(domain->prd); + if (domain->triclinic) + PlaneObj(6).draw(image, flag, reg.color, center, myreg->normal, domain->boxlo_bound, + domain->boxhi_bound, scale, myreg, reg.diameter, opacity); + else + PlaneObj(6).draw(image, flag, reg.color, center, myreg->normal, domain->boxlo, + domain->boxhi, scale, myreg, reg.diameter, opacity); + } else if (regstyle == "sphere") { auto *myreg = dynamic_cast(reg.ptr); // inconsistent style. should not happen. if (!myreg) continue; - double center[3]; - center[0] = myreg->xc; - center[1] = myreg->yc; - center[2] = myreg->zc; + double center[3] = {myreg->xc, myreg->yc, myreg->zc}; if (reg.style == FRAME) { - double radius[3] = {myreg->radius,myreg->radius,myreg->radius}; - ellipsoid2wireframe(image, 4, reg.color, reg.diameter, center, radius, reg.ptr); + // use wireframe mode of ellipsoid with three identical radii at a medium mesh level + double radius[3] = {myreg->radius, myreg->radius, myreg->radius}; + EllipsoidObj(4).draw(image, 2, reg.color, center, radius, reg.ptr, reg.diameter, 1.0); } else if ((reg.style == FILLED) || (reg.style == TRANSPARENT)) { double opacity = (reg.style == TRANSPARENT) ? reg.opacity : 1.0; myreg->forward_transform(center[0], center[1], center[2]); image->draw_sphere(center, reg.color, 2.0 * myreg->radius, opacity); } + } else { if (comm->me == 0) error->warning(FLERR, "Region style {} is not yet supported by dump image", regstyle); } } } + // clang-format off // render outline of my sub-box, orthogonal or triclinic @@ -2563,13 +2212,32 @@ void DumpImage::unpack_forward_comm(int n, int first, double *buf) /* ---------------------------------------------------------------------- */ +void *DumpImage::extract(const char *str, int &dim) +{ + dim=0; + if (strcmp(str,"image") == 0) { + return image; + } + return nullptr; +} + +/* ---------------------------------------------------------------------- */ + int DumpImage::modify_param(int narg, char **arg) { int n = DumpCustom::modify_param(narg,arg); if (n) return n; + // determine offset in list of arguments for error pointer. also handle the no match case. + int argoff = 0; + while (input && input->arg[argoff] && input->arg[argoff+1]) { + if ((strcmp(input->arg[argoff], arg[0]) == 0) && (strcmp(input->arg[argoff+1], arg[1]) == 0)) + break; + ++argoff; + } + if (strcmp(arg[0],"acolor") == 0) { - if (narg < 3) error->all(FLERR,"Illegal dump_modify command"); + if (narg < 3) utils::missing_cmd_args(FLERR, "dump_modify acolor", error); int nlo,nhi; utils::bounds_typelabel(FLERR,arg[1],1,atom->ntypes,nlo,nhi,lmp,Atom::ATOM); @@ -2583,53 +2251,59 @@ int DumpImage::modify_param(int narg, char **arg) for (int i = nlo; i <= nhi; i++) { colortype[i] = image->color2rgb(colors[m%ncolors].c_str()); if (colortype[i] == nullptr) - error->all(FLERR,"Invalid color in dump_modify command"); + error->all(FLERR,argoff+2,"Invalid color in dump_modify acolor command {}", arg[2]); m++; } return 3; } if (strcmp(arg[0],"adiam") == 0) { - if (narg < 3) error->all(FLERR,"Illegal dump_modify command"); + if (narg < 3) utils::missing_cmd_args(FLERR, "dump_modify adiam", error); int nlo,nhi; utils::bounds_typelabel(FLERR,arg[1],1,atom->ntypes,nlo,nhi,lmp,Atom::ATOM); double diam = utils::numeric(FLERR,arg[2],false,lmp); - if (diam <= 0.0) error->all(FLERR,"Illegal dump_modify command"); + if (diam <= 0.0) + error->all(FLERR,argoff+2,"Illegal diameter {} in dump_modify adiam command", diam); for (int i = nlo; i <= nhi; i++) diamtype[i] = diam; return 3; } if (strcmp(arg[0],"atrans") == 0) { - if (narg < 3) error->all(FLERR,"Illegal dump_modify command"); + if (narg < 3) utils::missing_cmd_args(FLERR, "dump_modify atrans", error); int nlo,nhi; utils::bounds_typelabel(FLERR,arg[1],1,atom->ntypes,nlo,nhi,lmp,Atom::ATOM); double opacity = utils::numeric(FLERR,arg[2],false,lmp); - if ((opacity < 0.0) || (opacity > 1.0)) error->all(FLERR,"Illegal dump_modify command"); + if ((opacity < 0.0) || (opacity > 1.0)) + error->all(FLERR,argoff+2,"Illegal transparency {} in dump_modify atrans command", opacity); for (int i = nlo; i <= nhi; i++) aopacity[i] = opacity; return 3; } if ((strcmp(arg[0],"amap") == 0) || (strcmp(arg[0],"gmap") == 0)) { - if (narg < 6) error->all(FLERR,"Illegal dump_modify command"); - if (strlen(arg[3]) != 2) error->all(FLERR,"Illegal dump_modify command"); + if (narg < 6) utils::missing_cmd_args(FLERR, "dump_modify amap/gmap", error); + if (strlen(arg[3]) != 2) + error->all(FLERR,argoff+3, "Incorrect dump_modify amap/gmap colormap style {}", arg[3]); int factor = 0; if (arg[3][0] == 's') factor = 1; else if (arg[3][0] == 'c') factor = 2; else if (arg[3][0] == 'd') factor = 3; - else error->all(FLERR,"Illegal dump_modify command"); + else error->all(FLERR,argoff+3, "Unknown dump_modify amap/gmap color map type {}", arg[3][0]); int nentry = utils::inumeric(FLERR,arg[5],false,lmp); - if (nentry < 1) error->all(FLERR,"Illegal dump_modify command"); + if (nentry < 1) + error->all(FLERR, argoff+5, "Must have at least 1 color map entry for dump_modify amap/gmap"); n = 6 + factor*nentry; - if (narg < n) error->all(FLERR,"Illegal dump_modify command"); + if (narg < n) utils::missing_cmd_args(FLERR, "dump_modify amap/gmap", error); int flag = 0; - if (strcmp(arg[0],"amap") == 0) flag = image->map_reset(0,n-1,&arg[1]); - if (strcmp(arg[0],"gmap") == 0) flag = image->map_reset(1,n-1,&arg[1]); - if (flag) error->all(FLERR,"Illegal dump_modify command"); + if (strcmp(arg[0], "amap") == 0) flag = image->map_reset(0, n-1, &arg[1]); + if (strcmp(arg[0], "gmap") == 0) flag = image->map_reset(1, n-1, &arg[1]); + if (flag) + error->all(FLERR, argoff+flag, "Invalid map settings in dump_modify amap/gmap command"); + return n; } if (strcmp(arg[0],"bcolor") == 0) { - if (narg < 3) error->all(FLERR,"Illegal dump_modify command"); + if (narg < 3) utils::missing_cmd_args(FLERR, "dump_modify bcolor", error); if (atom->nbondtypes == 0) error->all(FLERR,"Dump modify bcolor not allowed with no bond types"); int nlo,nhi; @@ -2645,38 +2319,40 @@ int DumpImage::modify_param(int narg, char **arg) for (int i = nlo; i <= nhi; i++) { bcolortype[i] = image->color2rgb(colors[m%ncolors].c_str()); if (bcolortype[i] == nullptr) - error->all(FLERR,"Invalid color in dump_modify command"); + error->all(FLERR, argoff + 2, "Invalid color in dump_modify bcolor command"); m++; } return 3; } if (strcmp(arg[0],"bdiam") == 0) { - if (narg < 3) error->all(FLERR,"Illegal dump_modify command"); + if (narg < 3) utils::missing_cmd_args(FLERR, "dump_modify bdiam", error); if (atom->nbondtypes == 0) - error->all(FLERR,"Dump modify bdiam not allowed with no bond types"); + error->all(FLERR, argoff, "Dump modify bdiam not allowed with no bond types"); int nlo,nhi; utils::bounds_typelabel(FLERR,arg[1],1,atom->nbondtypes,nlo,nhi,lmp,Atom::BOND); double diam = utils::numeric(FLERR,arg[2],false,lmp); - if (diam <= 0.0) error->all(FLERR,"Illegal dump_modify command"); + if (diam <= 0.0) + error->all(FLERR, argoff + 2, "Invalid bdiam diameter in dump_modify command"); for (int i = nlo; i <= nhi; i++) bdiamtype[i] = diam; return 3; } if (strcmp(arg[0],"btrans") == 0) { - if (narg < 3) error->all(FLERR,"Illegal dump_modify command"); + if (narg < 3) utils::missing_cmd_args(FLERR, "dump_modify btrans", error); if (atom->nbondtypes == 0) error->all(FLERR,"Dump modify btrans not allowed with no bond types"); int nlo,nhi; utils::bounds_typelabel(FLERR,arg[1],1,atom->nbondtypes,nlo,nhi,lmp,Atom::BOND); double opacity = utils::numeric(FLERR,arg[2],false,lmp); - if ((opacity < 0.0) || (opacity > 1.0)) error->all(FLERR,"Illegal dump_modify command"); + if ((opacity < 0.0) || (opacity > 1.0)) + error->all(FLERR, argoff + 2, "Invalid btrans opacity in dump_modify command"); for (int i = nlo; i <= nhi; i++) bopacity[i] = opacity; return 3; } if (strcmp(arg[0],"backcolor") == 0) { - if (narg < 2) error->all(FLERR,"Illegal dump_modify command"); + if (narg < 2) utils::missing_cmd_args(FLERR, "dump_modify backcolor", error); double *color = image->color2rgb(arg[1]); if (color == nullptr) error->all(FLERR,"Invalid color in dump_modify command"); image->background[0] = static_cast(color[0]*255.0); @@ -2685,54 +2361,92 @@ int DumpImage::modify_param(int narg, char **arg) return 2; } + if (strcmp(arg[0],"backcolor2") == 0) { + if (narg < 2) utils::missing_cmd_args(FLERR, "dump_modify backcolor2", error); + if (strcmp(arg[1],"none") == 0) { + image->background2[0] = -1; + image->background2[1] = -1; + image->background2[2] = -1; + } else { + double *color = image->color2rgb(arg[1]); + if (color == nullptr) + error->all(FLERR, argoff + 1, "Invalid color in dump_modify backcolor2 command"); + image->background2[0] = static_cast(color[0]*255.0); + image->background2[1] = static_cast(color[1]*255.0); + image->background2[2] = static_cast(color[2]*255.0); + } + return 2; + } + if (strcmp(arg[0],"boxcolor") == 0) { - if (narg < 2) error->all(FLERR,"Illegal dump_modify command"); + if (narg < 2) utils::missing_cmd_args(FLERR, "dump_modify boxcolor", error); image->boxcolor = image->color2rgb(arg[1]); if (image->boxcolor == nullptr) - error->all(FLERR,"Invalid color in dump_modify command"); + error->all(FLERR, argoff + 1, "Invalid color in dump_modify boxcolor command"); return 2; } if (strcmp(arg[0],"boxtrans") == 0) { - if (narg < 2) error->all(FLERR,"Illegal dump_modify command"); + if (narg < 2) utils::missing_cmd_args(FLERR, "dump_modify boxtrans", error); boxopacity = utils::numeric(FLERR,arg[1],false,lmp); if ((boxopacity < 0.0) || (boxopacity > 1.0)) - error->all(FLERR,"Invalid boxtrans in dump_modify command"); + error->all(FLERR, argoff + 1, "Invalid boxtrans opacity in dump_modify command"); return 2; } if (strcmp(arg[0],"subboxtrans") == 0) { - if (narg < 2) error->all(FLERR,"Illegal dump_modify command"); + if (narg < 2) utils::missing_cmd_args(FLERR, "dump_modify subboxtrans", error); subboxopacity = utils::numeric(FLERR,arg[1],false,lmp); if ((subboxopacity < 0.0) || (subboxopacity > 1.0)) - error->all(FLERR,"Invalid subboxtrans in dump_modify command"); + error->all(FLERR, argoff + 1, "Invalid subboxtrans opacity in dump_modify command"); return 2; } if (strcmp(arg[0],"axestrans") == 0) { - if (narg < 2) error->all(FLERR,"Illegal dump_modify command"); + if (narg < 2) utils::missing_cmd_args(FLERR, "dump_modify axestrans", error); axesopacity = utils::numeric(FLERR,arg[1],false,lmp); if ((axesopacity < 0.0) || (axesopacity > 1.0)) - error->all(FLERR,"Invalid axestrans in dump_modify command"); + error->all(FLERR, argoff + 1, "Invalid axestrans opacity in dump_modify command"); return 2; } if (strcmp(arg[0],"color") == 0) { - if (narg < 5) error->all(FLERR,"Illegal dump_modify command"); + if (narg < 5) utils::missing_cmd_args(FLERR, "dump_modify color", error); int flag = image->addcolor(arg[1],utils::numeric(FLERR,arg[2],false,lmp), utils::numeric(FLERR,arg[3],false,lmp), utils::numeric(FLERR,arg[4],false,lmp)); - if (flag) error->all(FLERR,"Illegal dump_modify command"); + if (flag) error->all(FLERR, argoff + 1 + flag, "Incorrect dump_modify color command"); return 5; } if (strcmp(arg[0],"fcolor") == 0) { - if (narg < 3) error->all(FLERR,"Illegal dump_modify command"); + if (narg < 3) utils::missing_cmd_args(FLERR, "dump_modify fcolor", error); auto *color = image->color2rgb(arg[2]); - if (!color) error->all(FLERR, "Unknown color for dump_modify fcolor: {}", arg[2]); + if (!color) error->all(FLERR, argoff + 2, "Unknown color for dump_modify fcolor: {}", arg[2]); + bool match = false; + for (auto &ifix : fixes) { + if (ifix.id == arg[1]) { + ifix.rgb = color; + match = true; + } + } + if (!match) error->all(FLERR, argoff + 1, "Fix ID {} is not included in dump {}", arg[1], id); + return 3; + } + + if (strcmp(arg[0],"ftrans") == 0) { + if (narg < 3) utils::missing_cmd_args(FLERR, "dump_modify ftrans", error); + double opacity = utils::numeric(FLERR,arg[2],false,lmp); + if ((opacity < 0.0) || (opacity > 1.0)) + error->all(FLERR, argoff + 2, "Illegal transparency {} for dump_modify ftrans", arg[2]); + bool match = false; for (auto &ifix : fixes) { - if (ifix.id == arg[1]) ifix.rgb = color; + if (ifix.id == arg[1]) { + ifix.opacity = opacity; + match = true; + } } + if (!match) error->all(FLERR, argoff + 1, "Fix ID {} is not included in dump {}", arg[1], id); return 3; } diff --git a/src/dump_image.h b/src/GRAPHICS/dump_image.h similarity index 72% rename from src/dump_image.h rename to src/GRAPHICS/dump_image.h index 80efc057543..7da765af7e1 100644 --- a/src/dump_image.h +++ b/src/GRAPHICS/dump_image.h @@ -26,6 +26,7 @@ namespace LAMMPS_NS { // forward declarations class AtomVecBody; +class AtomVecEllipsoid; class AtomVecLine; class AtomVecTri; class Compute; @@ -38,21 +39,13 @@ class Region; class DumpImage : public DumpCustom { public: - int multifile_override; // used by write_dump command - enum { - SPHERE, // a single sphere with radius provided - LINE, // a cylinder with diameter given through fflag2 - TRI, // a surface mesh as triangles or cylinder mesh based on fflag1, fflag2 sets diameter - CYLINDER, // a cylinder with diameter given by fix, fflag1 choose caps, fflag2 adjusts diameter - TRIANGLE, // a regular triangle, no settings apply - BOND // two connected cylinders with bond diameter, colored by atom types, fflag1 sets cap - }; // used by some Body and Fix child classes - DumpImage(LAMMPS *, int, char **); ~DumpImage() override; int pack_forward_comm(int, int *, double *, int, int *) override; void unpack_forward_comm(int, int, double *) override; + void *extract(const char *, int &) override; + protected: int filetype; enum { PPM, JPG, PNG }; // file type constants @@ -67,6 +60,10 @@ class DumpImage : public DumpCustom { int triflag; // 0/1 for draw atoms as triangles int tcolor, tstyle; // what determines color/style of tris double tdiamvalue; // tri edge diameter value + int ellipsoidflag; // 0/1 for draw atoms as ellipsoids + int ecolor, estyle; // what determines color/style of ellipsoid + int elevel; // mesh refinement level 1, 2, 3, or 4 + double ediamvalue; // ellipsoid edge diameter value int bodyflag; // 0/1 for draw atoms as bodies int bodycolor; // what determines color of bodies double bodyflag1, bodyflag2; // user-specified params for drawing bodies @@ -76,20 +73,20 @@ class DumpImage : public DumpCustom { double bdiamvalue; // bond diameter value double bondcutoff; // autobond cutoff - int extraflag; // 0/1 for any of line/tri/body flag set - char *thetastr, *phistr; // variables for view theta,phi - int thetavar, phivar; // index to theta,phi vars - int cflag; // static/dynamic box center - double cx, cy, cz; // fractional box center - char *cxstr, *cystr, *czstr; // variables for box center - int cxvar, cyvar, czvar; // index to box center vars - char *upxstr, *upystr, *upzstr; // view up vector variables - int upxvar, upyvar, upzvar; // index to up vector vars - char *zoomstr; // view zoom variable name - int zoomvar; // index to zoom variable - int boxflag, axesflag; // 0/1 for draw box and axes - double boxdiam, axeslen, axesdiam; // params for drawing box and axes - double boxopacity, axesopacity, subboxopacity; // opacity for box, subbox, axes + int extraflag; // 0/1 for any of line/tri/body flag set + char *thetastr, *phistr; // variables for view theta,phi + int thetavar, phivar; // index to theta,phi vars + int cflag; // static/dynamic box center + double cx, cy, cz; // fractional box center + char *cxstr, *cystr, *czstr; // variables for box center + int cxvar, cyvar, czvar; // index to box center vars + char *upxstr, *upystr, *upzstr; // view up vector variables + int upxvar, upyvar, upzvar; // index to up vector vars + char *zoomstr; // view zoom variable name + int zoomvar; // index to zoom variable + int boxflag, axesflag; // 0/1 for draw box and axes + double boxdiam, axeslen, axesdiam; // params for drawing box and axes + double boxopacity, axesopacity, subboxopacity; // opacity for box, subbox, axes int subboxflag; double subboxdiam; @@ -114,22 +111,25 @@ class DumpImage : public DumpCustom { AtomVecLine *avec_line; // ptrs to atom style (sub)classes AtomVecTri *avec_tri; + AtomVecEllipsoid *avec_ellipsoid; AtomVecBody *avec_body; struct FixInfo { FixInfo() = delete; FixInfo(const std::string &_id, Fix *_ptr, int _colorstyle, double _flag1, double _flag2, - double *_rgb) : - id(_id), ptr(_ptr), colorstyle(_colorstyle), flag1(_flag1), flag2(_flag2), rgb(_rgb) + double *_rgb, double _opacity = 1.0) : + id(_id), ptr(_ptr), colorstyle(_colorstyle), flag1(_flag1), flag2(_flag2), rgb(_rgb), + opacity(_opacity) { } - Fix *ptr; std::string id; + Fix *ptr; int colorstyle; double flag1; double flag2; double *rgb; + double opacity; }; std::vector fixes; diff --git a/src/dump_movie.cpp b/src/GRAPHICS/dump_movie.cpp similarity index 100% rename from src/dump_movie.cpp rename to src/GRAPHICS/dump_movie.cpp diff --git a/src/dump_movie.h b/src/GRAPHICS/dump_movie.h similarity index 100% rename from src/dump_movie.h rename to src/GRAPHICS/dump_movie.h diff --git a/src/GRAPHICS/fix_graphics_arrows.cpp b/src/GRAPHICS/fix_graphics_arrows.cpp new file mode 100644 index 00000000000..5f5c5936360 --- /dev/null +++ b/src/GRAPHICS/fix_graphics_arrows.cpp @@ -0,0 +1,443 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "fix_graphics_arrows.h" + +#include "atom.h" +#include "comm.h" +#include "compute_chunk_atom.h" +#include "domain.h" +#include "error.h" +#include "graphics.h" +#include "group.h" +#include "input.h" +#include "math_extra.h" +#include "memory.h" +#include "modify.h" +#include "update.h" +#include "variable.h" + +#include + +using namespace LAMMPS_NS; +using namespace FixConst; + +enum { NONE, DIPOLE, FORCE, VELOCITY, VARIABLE, CHUNK }; + +/* ---------------------------------------------------------------------- */ + +FixGraphicsArrows::FixGraphicsArrows(LAMMPS *lmp, int narg, char **arg) : + Fix(lmp, narg, arg), vec{0.0, 0.0, 0.0}, xstr(nullptr), ystr(nullptr), zstr(nullptr), + cchunk(nullptr), cpos(nullptr), cvec(nullptr), id_chunk(nullptr), id_pos(nullptr), + id_vec(nullptr), imgobjs(nullptr), imgparms(nullptr) +{ + if (narg < 7) utils::missing_cmd_args(FLERR, "fix graphics/arrows", error); + + // parse mandatory arg + + nevery = utils::inumeric(FLERR, arg[3], false, lmp); + if (nevery <= 0) error->all(FLERR, 3, "Illegal fix graphics/arrows nevery value"); + global_freq = nevery; + dynamic_group_allow = 1; + scalar_flag = 1; + extscalar = 0; + + mode = NONE; + numobjs = 0; + varflag = 0; + scale = 1.0; + autoscale = false; + autovalue = 1.0; + + int iarg = 7; + if (strcmp(arg[4], "dipole") == 0) { + mode = DIPOLE; + } else if (strcmp(arg[4], "force") == 0) { + mode = FORCE; + } else if (strcmp(arg[4], "velocity") == 0) { + mode = VELOCITY; + } else if (strcmp(arg[4], "variable") == 0) { + mode = VARIABLE; + if (narg < 9) utils::missing_cmd_args(FLERR, "fix graphics/arrows variable", error); + iarg = 9; + if (strstr(arg[5], "v_") == arg[5]) { + varflag = 1; + xstr = utils::strdup(arg[5] + 2); + } else { + vec[0] = utils::numeric(FLERR, arg[5], false, lmp); + } + if (strstr(arg[6], "v_") == arg[6]) { + varflag = 1; + ystr = utils::strdup(arg[6] + 2); + } else { + vec[1] = utils::numeric(FLERR, arg[6], false, lmp); + } + if (strstr(arg[7], "v_") == arg[7]) { + varflag = 1; + zstr = utils::strdup(arg[7] + 2); + } else { + vec[2] = utils::numeric(FLERR, arg[7], false, lmp); + } + radius = utils::numeric(FLERR, arg[8], false, lmp); + if (radius <= 0.0) error->all(FLERR, 6, "Arrow radius must be > 0"); + } else if (strcmp(arg[4], "chunk") == 0) { + mode = CHUNK; + if (narg < 10) utils::missing_cmd_args(FLERR, "fix graphics/arrows chunk", error); + iarg = 10; + + id_chunk = utils::strdup(arg[5]); + cchunk = dynamic_cast(modify->get_compute_by_id(id_chunk)); + if (!cchunk) + error->all(FLERR, 5, "Chunk/atom compute {} does not exist or is incorrect style for fix {}", + id_chunk, style); + + id_pos = utils::strdup(arg[6]); + cpos = modify->get_compute_by_id(id_pos); + if (!cpos) + error->all(FLERR, 6, "Per-chunk compute {} does not exist for fix graphics/arrows", id_pos); + + id_vec = utils::strdup(arg[7]); + cvec = modify->get_compute_by_id(id_vec); + if (!cvec) + error->all(FLERR, 7, "Per-chunk compute {} does not exist for fix graphics/arrows", id_vec); + + scale = utils::numeric(FLERR, arg[8], false, lmp); + radius = utils::numeric(FLERR, arg[9], false, lmp); + if (radius <= 0.0) error->all(FLERR, 9, "Arrow radius must be > 0"); + } else { + error->all(FLERR, 4, "Unknown fix graphics/arrows keyword: {}", arg[4]); + } + + // we have the same arguments for these modes + if ((mode == DIPOLE) || (mode == FORCE) || (mode == VELOCITY)) { + scale = utils::numeric(FLERR, arg[5], false, lmp); + radius = utils::numeric(FLERR, arg[6], false, lmp); + if (radius <= 0.0) error->all(FLERR, 6, "Arrow radius must be > 0"); + } + + // parse optional keywords + + while (iarg < narg) { + if (strcmp(arg[iarg], "autoscale") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix graphics/arrows autoscale", error); + autoscale = true; + autovalue = utils::numeric(FLERR, arg[iarg + 1], false, lmp); + if (autovalue <= 0.0) + error->all(FLERR, iarg + 1, "Fix graphics/arrow autoscale value must be > 0"); + iarg += 2; + } else { + error->all(FLERR, iarg, "Unknown fix graphics/arrows keyword: {}", arg[iarg]); + } + } + + // checks + + if ((mode == DIPOLE) && !atom->mu_flag) + error->all(FLERR, 4, "Fix graphics/arrows dipole mode requires atom attribute mu"); +} + +/* ---------------------------------------------------------------------- */ + +FixGraphicsArrows::~FixGraphicsArrows() +{ + memory->destroy(imgobjs); + memory->destroy(imgparms); + + delete[] xstr; + delete[] ystr; + delete[] zstr; + delete[] id_chunk; + delete[] id_pos; + delete[] id_vec; +} + +/* ---------------------------------------------------------------------- */ + +int FixGraphicsArrows::setmask() +{ + return END_OF_STEP; +} + +/* ---------------------------------------------------------------------- */ + +void FixGraphicsArrows::init() +{ + if (xstr) { + int ivar = input->variable->find(xstr); + if (ivar < 0) + error->all(FLERR, Error::NOLASTLINE, + "Variable name {} for fix graphics/arrows x value does not exist", xstr); + if ((input->variable->atomstyle(ivar) == 0) && (input->variable->equalstyle(ivar) == 0)) + error->all(FLERR, Error::NOLASTLINE, + "Fix graphics/arrows variable {} is not atom- or equal-style variable", xstr); + xvar = ivar; + } + if (ystr) { + int ivar = input->variable->find(ystr); + if (ivar < 0) + error->all(FLERR, Error::NOLASTLINE, + "Variable name {} for fix graphics/arrows y value does not exist", ystr); + if ((input->variable->atomstyle(ivar) == 0) && (input->variable->equalstyle(ivar) == 0)) + error->all(FLERR, Error::NOLASTLINE, + "Fix graphics/arrows variable {} is not atom- or equal-style variable", ystr); + yvar = ivar; + } + if (zstr) { + int ivar = input->variable->find(zstr); + if (ivar < 0) + error->all(FLERR, Error::NOLASTLINE, + "Variable name {} for fix graphics/arrows z value does not exist", zstr); + if ((input->variable->atomstyle(ivar) == 0) && (input->variable->equalstyle(ivar) == 0)) + error->all(FLERR, Error::NOLASTLINE, + "Fix graphics/arrows variable {} is not atom- or equal-style variable", zstr); + zvar = ivar; + } + + // refresh and check per-chunk computes + if (mode == CHUNK) { + cchunk = dynamic_cast(modify->get_compute_by_id(id_chunk)); + if (!cchunk) + error->all(FLERR, Error::NOLASTLINE, + "Chunk/atom compute {} does not exist or is incorrect style for fix {}", id_chunk, + style); + cpos = modify->get_compute_by_id(id_pos); + if (!cpos) + error->all(FLERR, Error::NOLASTLINE, + "Per-chunk compute {} does not exist for fix graphics/arrows", id_pos); + if (!cpos->array_flag || (cpos->size_array_cols < 3)) + error->all(FLERR, Error::NOLASTLINE, + "Per-chunk compute {} is not compatible with fix graphics/arrows", id_pos); + cvec = modify->get_compute_by_id(id_vec); + if (!cvec) + error->all(FLERR, Error::NOLASTLINE, + "Per-chunk compute {} does not exist for fix graphics/arrows", id_vec); + if (!cvec->array_flag || (cvec->size_array_cols < 3)) + error->all(FLERR, Error::NOLASTLINE, + "Per-chunk compute {} is not compatible with fix graphics/arrows", id_vec); + } + + end_of_step(); +} + +/* ---------------------------------------------------------------------- */ + +void FixGraphicsArrows::end_of_step() +{ + memory->destroy(imgobjs); + memory->destroy(imgparms); + + // evaluate variable or per-chunk computes if necessary, wrap with clear/add + + if (varflag || (mode == CHUNK)) modify->clearstep_compute(); + + const auto *const *const x = atom->x; + const auto *const *const f = atom->f; + const auto *const *const v = atom->v; + const auto *const *const mu = atom->mu; + const auto *const mask = atom->mask; + const auto *const type = atom->type; + const auto nlocal = atom->nlocal; + + // count (local) number of arrows + + if (mode != CHUNK) { + int n = 0; + for (int i = 0; i < nlocal; ++i) + if (mask[i] & groupbit) ++n; + + numobjs = n; + memory->create(imgobjs, numobjs, "fix_graphics_arrows:imgobjs"); + memory->create(imgparms, numobjs, 10, "fix_graphics_arrows:imgparms"); + double *xdata = nullptr; + double *ydata = nullptr; + double *zdata = nullptr; + + // update variable data, if needed + if (mode == VARIABLE) { + if (xstr) { + if (input->variable->atomstyle(xvar)) { + memory->create(xdata, nlocal, "fix_graphics_arrows:xdata"); + input->variable->compute_atom(xvar, igroup, xdata, 1, 0); + } else { + vec[0] = input->variable->compute_equal(xvar); + } + } + if (ystr) { + if (input->variable->atomstyle(yvar)) { + memory->create(ydata, nlocal, "fix_graphics_arrows:ydata"); + input->variable->compute_atom(yvar, igroup, ydata, 1, 0); + } else { + vec[1] = input->variable->compute_equal(yvar); + } + } + if (zstr) { + if (input->variable->atomstyle(zvar)) { + memory->create(zdata, nlocal, "fix_graphics_arrows:zdata"); + input->variable->compute_atom(zvar, igroup, zdata, 1, 0); + } else { + vec[2] = input->variable->compute_equal(zvar); + } + } + } + + // determine automatic scale value for enabled automatic scaling of vectors, if needed + + auto num = group->count(igroup); + if (autoscale && (num > 0)) { + double mysum = 0.0; + for (int i = 0; i < nlocal; ++i) { + if (mask[i] & groupbit) { + if (mode == DIPOLE) { + mysum += MathExtra::len3(mu[i]); + } else if (mode == FORCE) { + mysum += MathExtra::len3(f[i]); + } else if (mode == VELOCITY) { + mysum += MathExtra::len3(v[i]); + } else if (mode == VARIABLE) { + double myvec[3]; + if (xdata) + myvec[0] = xdata[i]; + else + myvec[0] = vec[0]; + if (ydata) + myvec[1] = ydata[i]; + else + myvec[1] = vec[1]; + if (zdata) + myvec[2] = zdata[i]; + else + myvec[2] = vec[2]; + mysum += MathExtra::len3(myvec); + } + } + } + + // compute average across all processors and invert (num was checked for != 0 above) + MPI_Allreduce(&mysum, &scale, 1, MPI_DOUBLE, MPI_SUM, world); + if (scale != 0.0) + scale = autovalue / (scale / static_cast(num)); + else + scale = 1.0; + } + + n = 0; + for (int i = 0; i < nlocal; ++i) { + if (mask[i] & groupbit) { + imgobjs[n] = Graphics::ARROW; + imgparms[n][0] = type[i]; + imgparms[n][1] = x[i][0]; + imgparms[n][2] = x[i][1]; + imgparms[n][3] = x[i][2]; + if (mode == DIPOLE) { + imgparms[n][4] = mu[i][0]; + imgparms[n][5] = mu[i][1]; + imgparms[n][6] = mu[i][2]; + } else if (mode == FORCE) { + imgparms[n][4] = f[i][0]; + imgparms[n][5] = f[i][1]; + imgparms[n][6] = f[i][2]; + } else if (mode == VELOCITY) { + imgparms[n][4] = v[i][0]; + imgparms[n][5] = v[i][1]; + imgparms[n][6] = v[i][2]; + } else if (mode == VARIABLE) { + if (xdata) + imgparms[n][4] = xdata[i]; + else + imgparms[n][4] = vec[0]; + if (ydata) + imgparms[n][5] = ydata[i]; + else + imgparms[n][5] = vec[1]; + if (zdata) + imgparms[n][6] = zdata[i]; + else + imgparms[n][6] = vec[2]; + } + imgparms[n][7] = scale; + imgparms[n][8] = 2.0 * radius; + imgparms[n][9] = 0.2; + ++n; + } + } + memory->destroy(xdata); + memory->destroy(ydata); + memory->destroy(zdata); + + } else { // mode == CHUNK + numobjs = cchunk->setup_chunks(); + memory->create(imgobjs, numobjs, "fix_graphics_arrows:imgobjs"); + memory->create(imgparms, numobjs, 10, "fix_graphics_arrows:imgparms"); + + // invoke per-chunk computes + if (!(cpos->invoked_flag & Compute::INVOKED_ARRAY)) { + cpos->compute_array(); + cpos->invoked_flag |= Compute::INVOKED_ARRAY; + } + if (!(cvec->invoked_flag & Compute::INVOKED_ARRAY)) { + cvec->compute_array(); + cvec->invoked_flag |= Compute::INVOKED_ARRAY; + } + + const double *const *const pdata = cpos->array; + const double *const *const vdata = cvec->array; + + // determine automatic scale value for enabled automatic scaling of vectors, if needed + + double mysum = 0.0; + if (autoscale) { + for (int n = 0; n < numobjs; ++n) mysum += MathExtra::len3(vdata[n]); + + if (mysum != 0.0) + scale = autovalue / (mysum / static_cast(numobjs)); + else + scale = 1.0; + } + for (int n = 0; n < numobjs; ++n) { + imgobjs[n] = Graphics::ARROW; + imgparms[n][0] = 1; + imgparms[n][1] = pdata[n][0]; + imgparms[n][2] = pdata[n][1]; + imgparms[n][3] = pdata[n][2]; + imgparms[n][4] = vdata[n][0]; + imgparms[n][5] = vdata[n][1]; + imgparms[n][6] = vdata[n][2]; + imgparms[n][7] = scale; + imgparms[n][8] = 2.0 * radius; + imgparms[n][9] = 0.2; + } + } + + if (varflag || (mode == CHUNK)) + modify->addstep_compute((update->ntimestep / nevery) * nevery + nevery); +} + +/* ---------------------------------------------------------------------- + current scale value determined for autoscalar or set statically +------------------------------------------------------------------------- */ + +double FixGraphicsArrows::compute_scalar() +{ + return scale; +} + +/* ---------------------------------------------------------------------- + provide graphics information to dump image +------------------------------------------------------------------------- */ + +int FixGraphicsArrows::image(int *&objs, double **&parms) +{ + objs = imgobjs; + parms = imgparms; + return numobjs; +} diff --git a/src/GRAPHICS/fix_graphics_arrows.h b/src/GRAPHICS/fix_graphics_arrows.h new file mode 100644 index 00000000000..1888089e779 --- /dev/null +++ b/src/GRAPHICS/fix_graphics_arrows.h @@ -0,0 +1,66 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef FIX_CLASS +// clang-format off +FixStyle(graphics/arrows,FixGraphicsArrows); +// clang-format on +#else + +#ifndef LMP_FIX_GRAPHICS_ARROWS_H +#define LMP_FIX_GRAPHICS_ARROWS_H + +#include "fix.h" + +namespace LAMMPS_NS { +class Compute; +class ComputeChunkAtom; + +class FixGraphicsArrows : public Fix { + public: + FixGraphicsArrows(class LAMMPS *, int, char **); + ~FixGraphicsArrows() override; + int setmask() override; + void init() override; + void end_of_step() override; + + double compute_scalar() override; + int image(int *&, double **&) override; + + protected: + int mode; + int varflag; + double scale; + double radius; + double vec[3]; + char *xstr; + char *ystr; + char *zstr; + int xvar, yvar, zvar; + + ComputeChunkAtom *cchunk; + Compute *cpos; + Compute *cvec; + char *id_chunk; + char *id_pos; + char *id_vec; + + bool autoscale; + double autovalue; + int numobjs; + int *imgobjs; + double **imgparms; +}; +} // namespace LAMMPS_NS +#endif +#endif diff --git a/src/GRAPHICS/fix_graphics_isosurface.cpp b/src/GRAPHICS/fix_graphics_isosurface.cpp new file mode 100644 index 00000000000..2fe92df34d7 --- /dev/null +++ b/src/GRAPHICS/fix_graphics_isosurface.cpp @@ -0,0 +1,1018 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "fix_graphics_isosurface.h" + +#include "arg_info.h" +#include "atom.h" +#include "comm.h" +#include "compute.h" +#include "domain.h" +#include "error.h" +#include "fix.h" +#include "graphics.h" +#include "input.h" +#include "math_extra.h" +#include "memory.h" +#include "modify.h" +#include "respa.h" +#include "update.h" +#include "variable.h" + +#include +#include +#include +#include + +using namespace LAMMPS_NS; + +namespace { +constexpr double BIG = 1.0e200; + +// for choosing the mesh resolution +enum { NONE, SURFMIN, SURFLOW, SURFMED, SURFHIGH, SURFMAX }; +constexpr double GRIDPOINTS[] = {0, 8.0, 16.0, 64.0, 128.0, 256.0}; + +// for choosing property +enum { NUMBER, MASS, CHARGE, VARIABLE, COMPUTE, FIX, CUSTOM }; + +// for truncating gaussian spreading +constexpr double CUTVAL = 9.210340371976182; // = -std::log(0.0001); +// extra grid points at the sides of the subbox grid +constexpr int GRIDEXTRA = 4; + +// custom data types for positions and triangles based on std::array +using vec3 = std::array; +using triangle = struct { + std::array triangle; + int type; +}; +using gridcell = struct { + vec3 pos[8]; + double iso[8]; +}; + +inline vec3 operator-(const vec3 &a, const vec3 &b) +{ + return {a[0] - b[0], a[1] - b[1], a[2] - b[2]}; +} + +// get vertex position for a grid cell edge by interpolating between +// the two corners based on their iso values +void get_vertex(const gridcell &g, int c1, int c2, vec3 &vert) +{ + const double diff = g.iso[c2] - g.iso[c1]; + const double fraction = (fabs(diff) > 0.0) ? -g.iso[c1] / diff : 0.0; + + vert[0] = g.pos[c1][0] + fraction * (g.pos[c2][0] - g.pos[c1][0]); + vert[1] = g.pos[c1][1] + fraction * (g.pos[c2][1] - g.pos[c1][1]); + vert[2] = g.pos[c1][2] + fraction * (g.pos[c2][2] - g.pos[c1][2]); +} + +// spread out atom data across the grid +void distribute(double ***pgrid, double ***dgrid, int ***tgrid, const double *pos, int type, + double val, const double *sublo, double delta, int nx, int ny, int nz, int nrange, + double rcutsq, double sigma) +{ + // locate the primary grid cell of the atom + const int ix = GRIDEXTRA + static_cast(floor((pos[0] - sublo[0]) / delta)); + const int iy = GRIDEXTRA + static_cast(floor((pos[1] - sublo[1]) / delta)); + const int iz = GRIDEXTRA + static_cast(floor((pos[2] - sublo[2]) / delta)); + + // compute relative position inside the cell + const double dx = (pos[0] - (sublo[0] + (ix - GRIDEXTRA) * delta)); + const double dy = (pos[1] - (sublo[1] + (iy - GRIDEXTRA) * delta)); + const double dz = (pos[2] - (sublo[2] + (iz - GRIDEXTRA) * delta)); + + // loop over possible grid positions for spreading the data +#if defined(_OPENMP) +#pragma omp parallel for +#endif + for (int jx = -nrange; jx <= nrange; ++jx) { + int kx = ix + jx; + // skip if outside the local grid + if ((kx < 0) || (kx >= nx)) continue; + for (int jy = -nrange; jy <= nrange; ++jy) { + int ky = iy + jy; + // skip if outside the local grid + if ((ky < 0) || (ky >= ny)) continue; + for (int jz = -nrange; jz <= nrange; ++jz) { + int kz = iz + jz; + // skip if outside the local grid + if ((kz < 0) || (kz >= nz)) continue; + + // compute squared distance of grid point to atom and apply cutoff and set value + double xpos = jx * delta - dx; + double ypos = jy * delta - dy; + double zpos = jz * delta - dz; + double distsq = xpos * xpos + ypos * ypos + zpos * zpos; + if (distsq < rcutsq) { + pgrid[kx][ky][kz] += val * exp(-distsq / sigma); + if (distsq < dgrid[kx][ky][kz]) { + dgrid[kx][ky][kz] = distsq; + tgrid[kx][ky][kz] = type; + } + } + } + } + } +} + +// for identifying the edges that contain the isosurface +constexpr int EDGETABLE[256] = { + 0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, + 0xd03, 0xe09, 0xf00, 0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, + 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, 0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, + 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, 0x3a0, 0x2a9, 0x1a3, 0xaa, + 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, 0x460, + 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, + 0xa69, 0xb60, 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, + 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c, + 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, + 0x2cf, 0x1c5, 0xcc, 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, 0x8c0, 0x9c9, + 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, + 0x7c0, 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55, 0x35f, 0x256, + 0x55a, 0x453, 0x759, 0x650, 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, + 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, + 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460, 0xca0, 0xda9, 0xea3, + 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, + 0x33, 0x339, 0x230, 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, + 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190, 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, + 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0}; + +// to map the "vertex" index from above to a list of triangles +// of the interpolated edge vertices. +constexpr int TRITABLE[256][16] = { + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, + {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, + {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, + {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, + {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, + {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, + {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, + {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, + {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, + {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, + {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, + {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, + {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, + {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, + {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, + {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, + {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, + {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, + {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, + {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, + {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, + {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, + {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, + {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, + {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, + {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, + {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, + {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, + {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, + {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, + {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, + {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, + {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, + {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, + {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, + {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, + {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, + {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, + {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, + {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, + {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, + {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, + {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, + {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, + {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, + {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, + {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, + {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, + {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, + {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, + {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, + {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, + {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, + {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, + {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, + {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, + {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, + {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, + {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, + {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, + {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, + {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, + {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, + {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, + {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, + {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, + {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, + {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, + {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, + {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, + {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, + {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, + {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, + {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, + {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, + {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, + {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, + {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, + {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, + {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, + {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, + {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, + {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, + {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, + {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, + {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, + {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, + {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, + {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, + {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, + {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, + {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, + {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, + {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, + {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, + {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, + {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}; +} // namespace + +/* ---------------------------------------------------------------------- */ + +FixGraphicsIsosurface::FixGraphicsIsosurface(LAMMPS *lmp, int narg, char **arg) : + Fix(lmp, narg, arg), pdata(nullptr), pstr(nullptr), pcomp(nullptr), pfix(nullptr), + imgobjs(nullptr), imgparms(nullptr) +{ + if (narg < 6) utils::missing_cmd_args(FLERR, "fix graphics/isosurface", error); + + // fix settings + global_freq = nevery; + dynamic_group_allow = 1; + comm_forward = 1; + nlevels_respa = 1; + nmax = -1; + + if (domain->triclinic) + error->all(FLERR, "Fix graphics/isosurface is currently not compatible with triclinic cells"); + if (domain->dimension == 2) + error->all(FLERR, "Fix graphics/isosurface is currently not compatible with 2d systems"); + + // defaults + numobjs = 0; + quality = SURFLOW; + pflag = NUMBER; + binary = 0; + pad = 0; + + // parse mandatory args + + nevery = utils::inumeric(FLERR, arg[3], false, lmp); + if (nevery <= 0) error->all(FLERR, 3, "Illegal fix graphics/isosurface nevery value {}", nevery); + iso = utils::numeric(FLERR, arg[4], false, lmp); + rad = 2.0 * utils::numeric(FLERR, arg[5], false, lmp); + if (rad <= 0.0) error->all(FLERR, 5, "Illegal fix graphics/isosurface radius value {}", rad); + + // parse optional args + + int iarg = 6; + while (iarg < narg) { + if (strcmp(arg[iarg], "quality") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix graphics/isosurface quality", error); + if (strcmp(arg[iarg + 1], "min") == 0) { + quality = SURFMIN; + } else if (strcmp(arg[iarg + 1], "low") == 0) { + quality = SURFLOW; + } else if (strcmp(arg[iarg + 1], "med") == 0) { + quality = SURFMED; + } else if (strcmp(arg[iarg + 1], "high") == 0) { + quality = SURFHIGH; + } else if (strcmp(arg[iarg + 1], "max") == 0) { + quality = SURFMAX; + } else { + error->all(FLERR, iarg + 1, "Unknown fix graphics/isosurface quality setting {}", + arg[iarg + 1]); + } + iarg += 2; + } else if (strcmp(arg[iarg], "property") == 0) { + if (iarg + 2 > narg) + utils::missing_cmd_args(FLERR, "fix graphics/isosurface property", error); + if (strcmp(arg[iarg + 1], "none") == 0) { + pflag = ArgInfo::NONE; + } else if (strcmp(arg[iarg + 1], "mass") == 0) { + pflag = ArgInfo::MASS; + } else { + ArgInfo argi(arg[iarg + 1]); + pflag = argi.get_type(); + pindex = argi.get_index1(); + delete[] pstr; + pstr = utils::strdup(argi.get_name()); + + // check validity of property argument + if ((pflag == ArgInfo::UNKNOWN) || (pflag == ArgInfo::NONE) || (argi.get_dim() > 1)) + error->all(FLERR, iarg + 1, "Invalid fix graphics/isosurface property {}", arg[iarg + 1]); + + if (pflag == ArgInfo::COMPUTE) { + pcomp = modify->get_compute_by_id(pstr); + if (!pcomp) + error->all(FLERR, iarg + 1, "Compute ID {} for fix graphics/isosurface does not exist", + pstr); + if (pcomp->peratom_flag == 0) + error->all(FLERR, iarg + 1, + "Fix graphics/isosurface compute {} does not calculate per-atom values", + pstr); + if (pindex == 0 && pcomp->size_peratom_cols != 0) + error->all(FLERR, iarg + 1, + "Fix graphics/isosurface compute {} does not calculate a per-atom vector", + pstr); + if (pindex && pcomp->size_peratom_cols == 0) + error->all(FLERR, iarg + 1, + "Fix graphics/isosurface compute {} does not calculate a per-atom array", + pstr); + if (pindex && pindex > pcomp->size_peratom_cols) + error->all(FLERR, iarg + 1, + "Fix graphics/isosurface compute {} array is accessed out-of-range{}", pstr, + utils::errorurl(20)); + + } else if (pflag == ArgInfo::FIX) { + pfix = modify->get_fix_by_id(pstr); + if (!pfix) + error->all(FLERR, iarg + 1, "Fix ID {} for fix graphics/isosurface does not exist", + pstr); + if (pfix->peratom_flag == 0) + error->all(FLERR, iarg + 1, + "Fix graphics/isosurface fix {} does not calculate per-atom values", pstr); + if (pindex == 0 && pfix->size_peratom_cols != 0) + error->all(FLERR, iarg + 1, + "Fix graphics/isosurface fix {} does not calculate a per-atom vector", pstr); + if (pindex && pfix->size_peratom_cols == 0) + error->all(FLERR, iarg + 1, + "Fix graphics/isosurface fix {} does not calculate a per-atom array", pstr); + if (pindex && pindex > pfix->size_peratom_cols) + error->all(FLERR, iarg + 1, + "Fix graphics/isosurface fix {} array is accessed out-of-range{}", pstr, + utils::errorurl(20)); + if (nevery % pfix->peratom_freq) + error->all(FLERR, iarg + 1, + "Fix {} for fix graphics/isosurface not computed at compatible time{}", pstr, + utils::errorurl(7)); + + } else if (pflag == ArgInfo::VARIABLE) { + pvar = input->variable->find(pstr); + if (pvar < 0) + error->all(FLERR, iarg + 1, + "Variable name {} for fix graphics/isosurface does not exist", pstr); + if (input->variable->atomstyle(pvar) == 0) + error->all(FLERR, iarg + 1, + "Fix graphics/isosurface variable {} is not atom-style variable", pstr); + } + } + iarg += 2; + } else if (strcmp(arg[iarg], "filename") == 0) { + if (iarg + 2 > narg) + utils::missing_cmd_args(FLERR, "fix graphics/isosurface filename", error); + filename = arg[iarg + 1]; + if (filename.find('*') == std::string::npos) + error->all(FLERR, iarg + 1, "Output to STL file requires file name with '*'"); + iarg += 2; + } else if (strcmp(arg[iarg], "binary") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix graphics/isosurface binary", error); + binary = utils::logical(FLERR, arg[iarg + 1], false, lmp); + iarg += 2; + } else if (strcmp(arg[iarg], "pad") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix graphics/isosurface pad", error); + pad = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); + iarg += 2; + } else { + error->all(FLERR, iarg, "Unknown fix graphics/isosurface keyword {}", arg[iarg]); + } + } +} + +/* ---------------------------------------------------------------------- */ + +FixGraphicsIsosurface::~FixGraphicsIsosurface() +{ + delete[] pstr; + memory->destroy(pdata); + memory->destroy(imgobjs); + memory->destroy(imgparms); +} + +/* ---------------------------------------------------------------------- */ + +int FixGraphicsIsosurface::setmask() +{ + int mask = 0; + mask |= FixConst::POST_FORCE; + mask |= FixConst::POST_FORCE_RESPA; + mask |= FixConst::END_OF_STEP; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixGraphicsIsosurface::init() +{ + if (utils::strmatch(update->integrate_style, "^respa")) + nlevels_respa = (dynamic_cast(update->integrate))->nlevels; + + // check validity of all compute, fix, or variable and update + + if (pflag == ArgInfo::COMPUTE) { + pcomp = modify->get_compute_by_id(pstr); + if (!pcomp) + error->all(FLERR, Error::NOLASTLINE, "Compute ID {} for fix ave/atom does not exist", pstr); + + } else if (pflag == ArgInfo::FIX) { + pfix = modify->get_fix_by_id(pstr); + if (!pfix) + error->all(FLERR, Error::NOLASTLINE, "Fix ID {} for fix ave/atom does not exist", pstr); + + } else if (pflag == ArgInfo::VARIABLE) { + pvar = input->variable->find(pstr); + if (pvar < 0) + error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix ave/atom does not exist", + pstr); + } + + // request updates of computes, if needed + if ((pflag != ArgInfo::NONE) && (pflag != ArgInfo::MASS)) { + bigint nextstep = (update->ntimestep / nevery) * nevery + nevery; + if ((nextstep - nevery) == update->ntimestep) nextstep = update->ntimestep; + modify->addstep_compute(nextstep); + } +} + +/* ---------------------------------------------------------------------- */ + +void FixGraphicsIsosurface::setup(int vflag) +{ + post_force(vflag); + end_of_step(); +} + +/* ---------------------------------------------------------------------- */ + +void FixGraphicsIsosurface::post_force(int /*vflag*/) +{ + // manage updates of computes, if needed + if ((pflag != ArgInfo::NONE) && (pflag != ArgInfo::MASS)) modify->clearstep_compute(); + + // ensure data storage is sufficient + if (nmax < atom->nmax) { + nmax = MAX(1, atom->nmax); + memory->destroy(pdata); + memory->create(pdata, nmax, "fix_graphics/isosurface:pdata"); + } + + // fill per-atom data storage with requested data for local atoms + const int nlocal = atom->nlocal; + const int *const type = atom->type; + const double *const mass = atom->mass; + const double *const rmass = atom->rmass; + + if (pflag == ArgInfo::NONE) { + for (int i = 0; i < nlocal; ++i) pdata[i] = 1.0; + } else if (pflag == ArgInfo::MASS) { + if (rmass) { + for (int i = 0; i < nlocal; ++i) pdata[i] = rmass[i]; + } else { + for (int i = 0; i < nlocal; ++i) pdata[i] = mass[type[i]]; + } + } else if (pflag == ArgInfo::COMPUTE) { + if (!(pcomp->invoked_flag & Compute::INVOKED_PERATOM)) { + pcomp->compute_peratom(); + pcomp->invoked_flag |= Compute::INVOKED_PERATOM; + } + if (pindex == 0) { + double *compute_vector = pcomp->vector_atom; + for (int i = 0; i < nlocal; ++i) pdata[i] = compute_vector[i]; + } else { + int jm1 = pindex - 1; + double **compute_array = pcomp->array_atom; + for (int i = 0; i < nlocal; ++i) pdata[i] = compute_array[i][jm1]; + } + } else if (pflag == ArgInfo::FIX) { + if (pindex == 0) { + double *fix_vector = pfix->vector_atom; + for (int i = 0; i < nlocal; ++i) pdata[i] = fix_vector[i]; + } else { + int jm1 = pindex - 1; + double **fix_array = pfix->array_atom; + for (int i = 0; i < nlocal; ++i) pdata[i] = fix_array[i][jm1]; + } + } else if (pflag == ArgInfo::VARIABLE) { + input->variable->compute_atom(pvar, igroup, pdata, 1, 0); + } + + // set data for ghost atoms + comm->forward_comm(this); + + // request updates of computes, if needed + if ((pflag != ArgInfo::NONE) && (pflag != ArgInfo::MASS)) { + bigint nextstep = (update->ntimestep / nevery) * nevery + nevery; + if ((nextstep - nevery) == update->ntimestep) nextstep = update->ntimestep; + modify->addstep_compute(nextstep); + } +} + +/* ---------------------------------------------------------------------- */ + +int FixGraphicsIsosurface::pack_forward_comm(int n, int *list, double *buf, int /*pbc_flag*/, + int * /*pbc*/) +{ + int m = 0; + for (int i = 0; i < n; ++i) buf[m++] = pdata[list[i]]; + + return m; +} + +/* ---------------------------------------------------------------------- */ + +void FixGraphicsIsosurface::unpack_forward_comm(int n, int first, double *buf) +{ + int m = 0; + int last = first + n; + + for (int i = first; i < last; ++i) pdata[i] = buf[m++]; +} + +/* ---------------------------------------------------------------------- */ + +void FixGraphicsIsosurface::post_force_respa(int vflag, int ilevel, int /*iloop*/) +{ + if (ilevel == nlevels_respa - 1) post_force(vflag); +} + +/* ---------------------------------------------------------------------- */ + +void FixGraphicsIsosurface::end_of_step() +{ + // determine grid dimensions + + const double *const sublo = domain->sublo; + const double *const subhi = domain->subhi; + + // get grid spacing and sub-domain lengths + double delta = cbrt(domain->xprd * domain->yprd * domain->zprd) / GRIDPOINTS[quality]; + double sublen[3] = {subhi[0] - sublo[0], subhi[1] - sublo[1], subhi[2] - sublo[2]}; + + // get grid dims for subdomains, add extra points on each side + int nx = 2 * GRIDEXTRA + static_cast(ceil(sublen[0] / delta)); + int ny = 2 * GRIDEXTRA + static_cast(ceil(sublen[1] / delta)); + int nz = 2 * GRIDEXTRA + static_cast(ceil(sublen[2] / delta)); + + // determine cutoff for spreading and number of gridpoints for spreading + const double rcutsq = rad * CUTVAL; + const int nrange = static_cast(ceil(sqrt(rcutsq) / delta)); + + // allocate grids and zero it out the property grid + double ***isogrid, ***distgrid; + int ***typegrid; + memory->create(isogrid, nx, ny, nz, "fix graphics/isosurface:isogrid"); + memory->create(distgrid, nx, ny, nz, "fix graphics/isosurface:distgrid"); + memory->create(typegrid, nx, ny, nz, "fix graphics/isosurface:typegrid"); + memset(isogrid[0][0], 0, sizeof(double) * nx * ny * nz); + memset(typegrid[0][0], 0, sizeof(int) * nx * ny * nz); + for (int ix = 0; ix < nx; ++ix) { + for (int iy = 0; iy < ny; ++iy) { + for (int iz = 0; iz < nz; ++iz) distgrid[ix][iy][iz] = BIG; + } + } + + // loop over local and ghost atoms, match group and check if within grid range + // per-atom data was copied to pdata array and sent to ghost atoms in post_force() + const int nall = atom->nlocal + atom->nghost; + const int *const mask = atom->mask; + const int *const type = atom->type; + const double *const *const x = atom->x; + for (int i = 0; i < nall; ++i) { + if (mask[i] & groupbit) + distribute(isogrid, distgrid, typegrid, x[i], type[i], pdata[i], sublo, delta, nx, ny, nz, + nrange, rcutsq, rad); + } + + // subtract the isovalue from grid data so that the isosurface would be drawn for a value of < 0.0 + for (int ix = 0; ix < nx; ++ix) { + for (int iy = 0; iy < ny; ++iy) { + for (int iz = 0; iz < nz; ++iz) isogrid[ix][iy][iz] -= iso; + } + } + + // now construct list of triangles for the isosurface using the marching cubes algorithm + // evaluate variable if necessary, wrap with clear/add + + gridcell g; + std::vector triangles; + + for (int ix = 0; ix < nx - 1; ++ix) { + for (int iy = 0; iy < ny - 1; ++iy) { + for (int iz = 0; iz < nz - 1; ++iz) { + // clang-format off + + // get lower edge coordinates of the grid cell + double gx = sublo[0] + (ix - GRIDEXTRA) * delta; + double gy = sublo[1] + (iy - GRIDEXTRA) * delta; + double gz = sublo[2] + (iz - GRIDEXTRA) * delta; + + // store position and isovalue for each corner of the grid cell + g.iso[0] = isogrid[ix ][iy ][iz ]; + g.iso[1] = isogrid[ix+1][iy ][iz ]; + g.iso[3] = isogrid[ix ][iy+1][iz ]; + g.iso[2] = isogrid[ix+1][iy+1][iz ]; + g.iso[4] = isogrid[ix ][iy ][iz+1]; + g.iso[5] = isogrid[ix+1][iy ][iz+1]; + g.iso[7] = isogrid[ix ][iy+1][iz+1]; + g.iso[6] = isogrid[ix+1][iy+1][iz+1]; + g.pos[0] = {gx, gy, gz}; + g.pos[1] = {gx+delta, gy, gz}; + g.pos[3] = {gx, gy+delta, gz}; + g.pos[2] = {gx+delta, gy+delta, gz}; + g.pos[4] = {gx, gy, gz+delta}; + g.pos[5] = {gx+delta, gy, gz+delta}; + g.pos[7] = {gx, gy+delta, gz+delta}; + g.pos[6] = {gx+delta, gy+delta, gz+delta}; + // clang-format on + + // determine edge table index + int idx = 0; + if (g.iso[0] < 0.0) idx |= 1; + if (g.iso[1] < 0.0) idx |= 2; + if (g.iso[2] < 0.0) idx |= 4; + if (g.iso[3] < 0.0) idx |= 8; + if (g.iso[4] < 0.0) idx |= 16; + if (g.iso[5] < 0.0) idx |= 32; + if (g.iso[6] < 0.0) idx |= 64; + if (g.iso[7] < 0.0) idx |= 128; + + // gridcube is not crossed by isosurface + if (EDGETABLE[idx] == 0) continue; + + // compute the possible 12 triangle vertices + std::array vertices; + if (EDGETABLE[idx] & 1) get_vertex(g, 0, 1, vertices[0]); + if (EDGETABLE[idx] & 2) get_vertex(g, 1, 2, vertices[1]); + if (EDGETABLE[idx] & 4) get_vertex(g, 2, 3, vertices[2]); + if (EDGETABLE[idx] & 8) get_vertex(g, 3, 0, vertices[3]); + if (EDGETABLE[idx] & 16) get_vertex(g, 4, 5, vertices[4]); + if (EDGETABLE[idx] & 32) get_vertex(g, 5, 6, vertices[5]); + if (EDGETABLE[idx] & 64) get_vertex(g, 6, 7, vertices[6]); + if (EDGETABLE[idx] & 128) get_vertex(g, 7, 4, vertices[7]); + if (EDGETABLE[idx] & 256) get_vertex(g, 0, 4, vertices[8]); + if (EDGETABLE[idx] & 512) get_vertex(g, 1, 5, vertices[9]); + if (EDGETABLE[idx] & 1024) get_vertex(g, 2, 6, vertices[10]); + if (EDGETABLE[idx] & 2048) get_vertex(g, 3, 7, vertices[11]); + + // compute the triangles for this grid cell and add them to the list + for (int i = 0; TRITABLE[idx][i] != -1; i += 3) + triangles.emplace_back( + triangle{{vertices[TRITABLE[idx][i]], vertices[TRITABLE[idx][i + 1]], + vertices[TRITABLE[idx][i + 2]]}, + typegrid[ix][iy][iz]}); + } + } + } + // we don't need the iso value grid anymore. + memory->destroy(isogrid); + + // allocate and assign list of graphics objects + numobjs = triangles.size(); + memory->destroy(imgobjs); + memory->destroy(imgparms); + memory->create(imgobjs, numobjs, "fix_graphics:imgobjs"); + memory->create(imgparms, numobjs, 10, "fix_graphics:imgparms"); + + int n = 0; + for (const auto &tri : triangles) { + // skip if any part of the triangle is outside the subdomain + bool addme = true; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + if ((tri.triangle[i][j] < sublo[j]) || (tri.triangle[i][j] > subhi[j])) addme = false; + if (addme) { + imgobjs[n] = Graphics::TRI; + imgparms[n][0] = tri.type; + imgparms[n][1] = tri.triangle[0][0]; + imgparms[n][2] = tri.triangle[0][1]; + imgparms[n][3] = tri.triangle[0][2]; + imgparms[n][4] = tri.triangle[1][0]; + imgparms[n][5] = tri.triangle[1][1]; + imgparms[n][6] = tri.triangle[1][2]; + imgparms[n][7] = tri.triangle[2][0]; + imgparms[n][8] = tri.triangle[2][1]; + imgparms[n][9] = tri.triangle[2][2]; + ++n; + } + } + numobjs = n; + + // write grid to STL format file, if requested + if (filename.size() > 0) { + int maxobjs = 0; + uint32_t allobjs = 0U; + MPI_Allreduce(&numobjs, &maxobjs, 1, MPI_INT, MPI_SUM, world); + allobjs = maxobjs; + MPI_Allreduce(&numobjs, &maxobjs, 1, MPI_INT, MPI_MAX, world); + + // convert to single precision data (for binary output) and compute normals + auto *stldata = new float[12 * maxobjs]; // 3 floats for normal and 9 floats for corners + double normal[3]; + vec3 d1, d2; + int n = 0; + for (const auto &tri : triangles) { + // skip if any part of the triangle is outside the subdomain + bool addme = true; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + if ((tri.triangle[i][j] < sublo[j]) || (tri.triangle[i][j] > subhi[j])) addme = false; + if (addme) { + d1 = tri.triangle[1] - tri.triangle[0]; + d2 = tri.triangle[2] - tri.triangle[0]; + MathExtra::cross3(d1.data(), d2.data(), normal); + MathExtra::norm3(normal); + stldata[12 * n] = normal[0]; + stldata[12 * n + 1] = normal[1]; + stldata[12 * n + 2] = normal[2]; + stldata[12 * n + 3] = tri.triangle[0][0]; + stldata[12 * n + 4] = tri.triangle[0][1]; + stldata[12 * n + 5] = tri.triangle[0][2]; + stldata[12 * n + 6] = tri.triangle[1][0]; + stldata[12 * n + 7] = tri.triangle[1][1]; + stldata[12 * n + 8] = tri.triangle[1][2]; + stldata[12 * n + 9] = tri.triangle[2][0]; + stldata[12 * n + 10] = tri.triangle[2][1]; + stldata[12 * n + 11] = tri.triangle[2][2]; + ++n; + } + } + + FILE *fp = nullptr; + if (comm->me == 0) { // only MPI rank 0 writes to the file + auto *filecurrent = utils::strdup(utils::star_subst(filename, update->ntimestep, pad)); + if (platform::has_compress_extension(filename)) { + if (binary) + error->one(FLERR, Error::NOLASTLINE, "Connot use compression with binary output: {}", + filename); + fp = platform::compressed_write(filecurrent); + } else if (binary) { + fp = fopen(filecurrent, "wb"); + } else { + fp = fopen(filecurrent, "w"); + } + if (fp == nullptr) + error->one(FLERR, Error::NOLASTLINE, "Cannot open STL output file {} for writing: {}", + filecurrent, utils::getsyserror()); + + auto title = fmt::format("STL isosurface from fix {} graphics/isosurface on step {}", id, + update->ntimestep); + title.resize(80, '\0'); + + // write out data from rank 0 + if (binary) { + uint16_t attributes = 0; + fwrite(title.c_str(), 1, 80, fp); + fwrite(&allobjs, sizeof(uint32_t), 1, fp); + for (int i = 0; i < numobjs; ++i) { + fwrite(stldata + 12 * i, sizeof(float), 12, fp); + fwrite(&attributes, sizeof(uint16_t), 1, fp); + } + } else { + fprintf(fp, "solid %s\n", title.c_str()); + for (int i = 0; i < numobjs; ++i) { + utils::print(fp, " facet normal {:e} {:e} {:e}\n", stldata[12 * i], stldata[12 * i + 1], + stldata[12 * i + 2]); + fputs(" outer loop\n", fp); + utils::print(fp, " vertex {:e} {:e} {:e}\n", stldata[12 * i + 3], + stldata[12 * i + 4], stldata[12 * i + 5]); + utils::print(fp, " vertex {:e} {:e} {:e}\n", stldata[12 * i + 6], + stldata[12 * i + 7], stldata[12 * i + 8]); + utils::print(fp, " vertex {:e} {:e} {:e}\n", stldata[12 * i + 9], + stldata[12 * i + 10], stldata[12 * i + 11]); + fputs(" endloop\n endfacet\n", fp); + } + } + + // receive data from other processes + MPI_Status status; + for (int j = 1; j < comm->nprocs; ++j) { + MPI_Recv(stldata, 12 * maxobjs, MPI_FLOAT, MPI_ANY_SOURCE, 0, world, &status); + int numtriangles = 0; + MPI_Get_count(&status, MPI_FLOAT, &numtriangles); + numtriangles /= 12; + + // write out received data + if (binary) { + uint16_t attributes = 0; + for (int i = 0; i < numtriangles; ++i) { + fwrite(stldata + 12 * i, sizeof(float), 12, fp); + fwrite(&attributes, sizeof(uint16_t), 1, fp); + } + } else { + for (int i = 0; i < numtriangles; ++i) { + utils::print(fp, " facet normal {:e} {:e} {:e}\n", stldata[12 * i], + stldata[12 * i + 1], stldata[12 * i + 2]); + fputs(" outer loop\n", fp); + utils::print(fp, " vertex {:e} {:e} {:e}\n", stldata[12 * i + 3], + stldata[12 * i + 4], stldata[12 * i + 5]); + utils::print(fp, " vertex {:e} {:e} {:e}\n", stldata[12 * i + 6], + stldata[12 * i + 7], stldata[12 * i + 8]); + utils::print(fp, " vertex {:e} {:e} {:e}\n", stldata[12 * i + 9], + stldata[12 * i + 10], stldata[12 * i + 11]); + fputs(" endloop\n endfacet\n", fp); + } + } + } + if (!binary) fprintf(fp, "endsolid %s\n", title.c_str()); + fclose(fp); + delete[] filecurrent; + } else { + MPI_Send(stldata, 12 * numobjs, MPI_FLOAT, 0, 0, world); + } + } +} + +/* ---------------------------------------------------------------------- + provide graphics information to dump image +------------------------------------------------------------------------- */ + +int FixGraphicsIsosurface::image(int *&objs, double **&parms) +{ + objs = imgobjs; + parms = imgparms; + return numobjs; +} diff --git a/src/GRAPHICS/fix_graphics_isosurface.h b/src/GRAPHICS/fix_graphics_isosurface.h new file mode 100644 index 00000000000..76b77ce172f --- /dev/null +++ b/src/GRAPHICS/fix_graphics_isosurface.h @@ -0,0 +1,68 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef FIX_CLASS +// clang-format off +FixStyle(graphics/isosurface,FixGraphicsIsosurface); +// clang-format on +#else + +#ifndef LMP_FIX_GRAPHICS_ISOSURFACE_H +#define LMP_FIX_GRAPHICS_ISOSURFACE_H + +#include "fix.h" + +namespace LAMMPS_NS { + +class FixGraphicsIsosurface : public Fix { + public: + FixGraphicsIsosurface(class LAMMPS *, int, char **); + ~FixGraphicsIsosurface() override; + + int setmask() override; + void init() override; + void setup(int) override; + void post_force(int) override; + void post_force_respa(int, int, int) override; + int pack_forward_comm(int, int *, double *, int, int *) override; + void unpack_forward_comm(int, int, double *) override; + void end_of_step() override; + + int image(int *&, double **&) override; + + private: + double iso; + double rad; + double *pdata; // holding space for per-atom property data + int quality; + int nlevels_respa; + int nmax; + + int pflag; // type of property data + int pindex; // 1-based index if data is vector, else 0 + char *pstr; // compute/fix/variable ID + class Compute *pcomp; // pointer to per-atom compute + class Fix *pfix; // pointer to per-atom fix + int pvar; // property variable index + + int binary; + int pad; + std::string filename; + + int numobjs; + int *imgobjs; + double **imgparms; +}; +} // namespace LAMMPS_NS +#endif +#endif diff --git a/src/GRAPHICS/fix_graphics_labels.cpp b/src/GRAPHICS/fix_graphics_labels.cpp new file mode 100644 index 00000000000..fd46f79a0cf --- /dev/null +++ b/src/GRAPHICS/fix_graphics_labels.cpp @@ -0,0 +1,749 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "fix_graphics_labels.h" + +#include "atom.h" +#include "comm.h" +#include "error.h" +#include "graphics.h" +#include "input.h" +#include "memory.h" +#include "modify.h" +#include "respa.h" +#include "text_file_reader.h" +#include "tokenizer.h" +#include "update.h" +#include "variable.h" + +#include +#include +#include + +#ifdef LAMMPS_JPEG +#include +#endif + +#ifdef LAMMPS_PNG +#include +#include +#include +#endif + +#include "scalable_font.h" + +using namespace LAMMPS_NS; +using namespace FixConst; + +namespace { + +void get_color(const std::string &color, unsigned char *rgb) +{ + if (color == "white") { + rgb[0] = 255; + rgb[1] = 255; + rgb[2] = 255; + } else if (color == "black") { + rgb[0] = 0; + rgb[1] = 0; + rgb[2] = 0; + } else if (color == "silver") { + rgb[0] = 192; + rgb[1] = 192; + rgb[2] = 192; + } else if (color == "darkgray") { + rgb[0] = 64; + rgb[1] = 64; + rgb[2] = 64; + } else { + auto val = ValueTokenizer(color, "/"); + auto tmp = val.next_int(); + if ((tmp < 0) || (tmp > 255)) throw TokenizerException("Invalid RGB value", color); + rgb[0] = (unsigned char) tmp; + tmp = val.next_int(); + if ((tmp < 0) || (tmp > 255)) throw TokenizerException("Invalid RGB value", color); + rgb[1] = (unsigned char) tmp; + tmp = val.next_int(); + if ((tmp < 0) || (tmp > 255)) throw TokenizerException("Invalid RGB value", color); + rgb[2] = (unsigned char) tmp; + if (val.has_next()) throw TokenizerException("Extra token", val.next_string()); + } +} + +// read image into buffer that is locally allocated with new +// return null pointer if incompatible format or not supported + +unsigned char *read_image(FILE *fp, int &width, int &height, const std::string &filename, + std::string &info) +{ + if (!fp) return nullptr; + unsigned char *pixmap = nullptr; + + if (utils::strmatch(filename, "\\.jpg$") || utils::strmatch(filename, "\\.JPG$") || + utils::strmatch(filename, "\\.jpeg$") || utils::strmatch(filename, "\\.JPEG$")) { + +#if defined(LAMMPS_JPEG) + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + + // initialize for reading from input stream + jpeg_create_decompress(&cinfo); + cinfo.err = jpeg_std_error(&jerr); + cinfo.out_color_space = JCS_RGB; + jpeg_stdio_src(&cinfo, fp); + + int rv = jpeg_read_header(&cinfo, TRUE); + if (rv != JPEG_HEADER_OK) return nullptr; + + jpeg_start_decompress(&cinfo); + + // we currently only can handle 8-bit 3-component images + if ((cinfo.data_precision != 8) || (cinfo.output_components != 3)) return nullptr; + + // read file line-by-line and convert to RGB buffer + width = cinfo.output_width; + height = cinfo.output_height; + pixmap = new unsigned char[3 * width * height]; + auto **scanline = new unsigned char *[height]; + for (int i = 0; i < height; ++i) { + scanline[0] = &pixmap[(height - 1 - i) * 3 * width]; + jpeg_read_scanlines(&cinfo, scanline, 1); + } + delete[] scanline; + jpeg_destroy_decompress(&cinfo); + + info = fmt::format("{}x{} JPEG file, 8-bit RGB", width, height); + return pixmap; +#else + info = "JPEG image format not supported in this LAMMPS binary"; + return nullptr; +#endif + + } else if (utils::strmatch(filename, "\\.png$") || utils::strmatch(filename, "\\.PNG$")) { + +#if defined(LAMMPS_PNG) + png_structp png_ptr = nullptr; + png_infop info_ptr = nullptr; + unsigned char sig[8]; + + // read and check PNG file signature + fread(sig, sizeof(unsigned char), 8, fp); + if (!png_check_sig(sig, 8)) return nullptr; + + // set up reading from file + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!png_ptr) return nullptr; + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, nullptr, nullptr); + return nullptr; + } + + // set up error handling + if (setjmp(png_jmpbuf(png_ptr))) { // NOLINT + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + delete[] pixmap; + return nullptr; + } + + png_uint_32 pngwidth, pngheight; + int bit_depth, color_type, interlace_type; + + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, 8); /* we already read the 8 signature bytes */ + png_read_info(png_ptr, info_ptr); /* read all PNG info up to image data */ + png_get_IHDR(png_ptr, info_ptr, &pngwidth, &pngheight, &bit_depth, &color_type, &interlace_type, + nullptr, nullptr); + width = pngwidth; + height = pngheight; + + if ((bit_depth != 8) || (color_type != PNG_COLOR_TYPE_RGB)) + info = fmt::format("{}x{} PNG file, Converted to 8-bit RGB", width, height); + else + info = fmt::format("{}x{} PNG file, 8-bit RGB", width, height); + + // convert data to compatible RGB data while reading + if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand(png_ptr); + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr); + if (bit_depth == 16) png_set_strip_16(png_ptr); + if ((color_type == PNG_COLOR_TYPE_GRAY) || (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) + png_set_gray_to_rgb(png_ptr); + png_set_strip_alpha(png_ptr); + png_set_interlace_handling(png_ptr); + + png_read_update_info(png_ptr, info_ptr); + + int rowbytes = png_get_rowbytes(png_ptr, info_ptr); + int channels = (int) png_get_channels(png_ptr, info_ptr); + // sanity check + if ((channels != 3) || (rowbytes != 3 * width)) return nullptr; + + pixmap = new unsigned char[height * width * 3]; + auto *row_pointers = new png_bytep[height]; + for (int i = 0; i < height; ++i) row_pointers[i] = pixmap + (height - 1 - i) * rowbytes; + + png_read_image(png_ptr, row_pointers); + png_read_end(png_ptr, nullptr); + + // cleanup + delete[] row_pointers; + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + + return pixmap; +#else + info = "PNG image format not supported in this LAMMPS binary"; + return nullptr; +#endif + + } else { + + // read file in NetPBM binary or ASCII format + char buffer[128]; + char *ptr = fgets(buffer, 128, fp); + if (!ptr || (strlen(buffer) < 3) || (buffer[0] != 'P')) return nullptr; + + // detect binary versus ASCII variant + bool binary = true; + if (buffer[1] == '3') + binary = false; + else if (buffer[1] != '6') + return nullptr; + + // skip over optional comments + do { + (void) fgets(buffer, 128, fp); + } while (buffer[0] == '#'); + + int rv = sscanf(buffer, "%d%d", &width, &height); + if (rv != 2) return nullptr; + + int tmp = 0; + ptr = fgets(buffer, 128, fp); + rv = sscanf(buffer, "%d", &tmp); + if ((rv != 1) || (tmp != 255)) return nullptr; + + info = fmt::format("{}x{} PPM {} file, 8-bit RGB", width, height, binary ? "binary" : "text"); + pixmap = new unsigned char[3 * width * height]; + if (binary) { + // read raw data directly into buffer in the expected order of lines + // this is the inverse of what Image::write_PPM() does + for (int y = height - 1; y >= 0; --y) { + rv = fread(&pixmap[y * width * 3], 3, width, fp); + if (rv != width) { + delete[] pixmap; + return nullptr; + } + } + } else { + // read file line-by-line and store three RGB values at a time + auto reader = TextFileReader(fp, "NetPBM ASCII pixmap"); + try { + int y = height - 1; + int x = 0; + auto *line = reader.next_line(); + while (line) { + auto values = ValueTokenizer(line); + + while (values.has_next()) { + pixmap[y * width * 3 + 3 * x] = values.next_int(); + pixmap[y * width * 3 + 3 * x + 1] = values.next_int(); + pixmap[y * width * 3 + 3 * x + 2] = values.next_int(); + ++x; + // next line of pixels + if (x >= width) { + --y; + x = 0; + } + } + line = reader.next_line(); + } + } catch (std::exception &e) { + delete[] pixmap; + return nullptr; + } + } + + return pixmap; + } + info = "Unknown image file format."; + return nullptr; +} +} // namespace + +/* ---------------------------------------------------------------------- */ + +#define PARSE_VARIABLE(value, name, index) \ + if (strstr(arg[index], "v_") == arg[index]) { \ + varflag = 1; \ + name = utils::strdup(arg[index] + 2); \ + } else \ + value = utils::numeric(FLERR, arg[index], false, lmp) + +FixGraphicsLabels::FixGraphicsLabels(LAMMPS *lmp, int narg, char **arg) : + Fix(lmp, narg, arg), imgobjs(nullptr), imgparms(nullptr) +{ + if (narg < 4) utils::missing_cmd_args(FLERR, "fix graphics/labels", error); + + // parse mandatory arg + + nevery = utils::inumeric(FLERR, arg[3], false, lmp); + if (nevery <= 0) error->all(FLERR, 3, "Illegal fix graphics/labels nevery value"); + global_freq = nevery; + dynamic_group_allow = 1; + + // set defaults + numobjs = 0; + varflag = 0; + + int iarg = 4; + while (iarg < narg) { + if (strcmp(arg[iarg], "image") == 0) { + if (iarg + 5 > narg) utils::missing_cmd_args(FLERR, "fix graphics/labels image", error); + + // clang-format off + PixmapInfo pix{"", 0.0, {0.0, 0.0, 0.0}, 0, 0, nullptr, {-1.0, -1.0, -1.0}, 1.0, + -1, -1, -1, -1, nullptr, nullptr, nullptr, nullptr}; + // clang-format on + + // read and store image file with pixmap only on MPI rank 0. + // must always open in binary mode to avoid data corruption on Windows + if (comm->me == 0) { + pix.filename = arg[iarg + 1]; + FILE *fp = fopen(pix.filename.c_str(), "rb"); + if (!fp) + error->one(FLERR, iarg + 1, "Cannot open fix graphics/labels image file {}: {}", + pix.filename, utils::getsyserror()); + pix.timestamp = platform::file_write_time(pix.filename); + std::string info; + pix.pixmap = read_image(fp, pix.width, pix.height, pix.filename, info); + fclose(fp); + if (!pix.pixmap) + error->one(FLERR, iarg + 1, "Reading fix graphics/labels image file {} failed: {}", + pix.filename, info); + + utils::logmesg(lmp, "Read image from {} file: {} format\n", pix.filename, info); + } + PARSE_VARIABLE(pix.pos[0], pix.xstr, iarg + 2); + PARSE_VARIABLE(pix.pos[1], pix.ystr, iarg + 3); + PARSE_VARIABLE(pix.pos[2], pix.zstr, iarg + 4); + iarg += 5; + + // check remaining arguments for optional image arguments + while (iarg < narg) { + // next argument is next keyword; exit loop + if ((strcmp(arg[iarg], "image") == 0) || (strcmp(arg[iarg], "text") == 0)) break; + + if (strcmp(arg[iarg], "scale") == 0) { + if (iarg + 2 > narg) + utils::missing_cmd_args(FLERR, "fix graphics/labels image scale", error); + PARSE_VARIABLE(pix.scale, pix.sstr, iarg + 1); + iarg += 2; + } else if (strcmp(arg[iarg], "transcolor") == 0) { + if (iarg + 2 > narg) + utils::missing_cmd_args(FLERR, "fix graphics/labels image transcolor", error); + if (strcmp(arg[iarg + 1], "auto") == 0) { + if (pix.pixmap) { + pix.transcolor[0] = pix.pixmap[0]; + pix.transcolor[1] = pix.pixmap[1]; + pix.transcolor[2] = pix.pixmap[2]; + } + } else if (strcmp(arg[iarg + 1], "none") == 0) { + pix.transcolor[0] = -255.0; + pix.transcolor[1] = -255.0; + pix.transcolor[2] = -255.0; + } else { + try { + unsigned char rgb[3]; + get_color(arg[iarg + 1], rgb); + pix.transcolor[0] = rgb[0]; + pix.transcolor[1] = rgb[1]; + pix.transcolor[2] = rgb[2]; + } catch (TokenizerException &e) { + error->all(FLERR, iarg + 1, "Error parsing RGB color value {}: {}", arg[iarg + 1], + e.what()); + } + } + iarg += 2; + } else { + error->all(FLERR, iarg, "Unknown fix graphics/labels image keyword: {}", arg[iarg]); + } + } + pixmaps.emplace_back(pix); + + } else if (strcmp(arg[iarg], "text") == 0) { + if (iarg + 5 > narg) utils::missing_cmd_args(FLERR, "fix graphics/labels text", error); + + // clang-format off + TextInfo txt{"", {0.0, 0.0, 0.0}, 0, 0, nullptr, {255, 255, 255}, {192, 192, 192}, + {192, 192, 192}, {192, 192, 192}, false, 48.0, 0.5, + -1, -1, -1, -1, nullptr, nullptr, nullptr, nullptr}; + // clang-format on + + txt.text = arg[iarg + 1]; + if (txt.text.find('$') != std::string::npos) varflag = 1; + + PARSE_VARIABLE(txt.pos[0], txt.xstr, iarg + 2); + PARSE_VARIABLE(txt.pos[1], txt.ystr, iarg + 3); + PARSE_VARIABLE(txt.pos[2], txt.zstr, iarg + 4); + iarg += 5; + + // check remaining arguments for optional image arguments + while (iarg < narg) { + // next argument is next keyword; exit loop + if ((strcmp(arg[iarg], "image") == 0) || (strcmp(arg[iarg], "text") == 0)) break; + + if (strcmp(arg[iarg], "size") == 0) { + if (iarg + 2 > narg) + utils::missing_cmd_args(FLERR, "fix graphics/labels text size", error); + PARSE_VARIABLE(txt.size, txt.sstr, iarg + 1); + // for sizes 4 to 64, text is rendered at 2x2 size and scaled down for anti-aliasing. + // for larger sizes, the image is rendered at max supported size and scaled as needed. + txt.size *= 2.0; + if ((txt.size < 8.0) || (txt.size > 1024.0)) + error->all(FLERR, iarg + 1, "Invalid fix graphics/labels text size value: {}", + txt.size * 0.5); + if (txt.size > 128.0) { + txt.scale = txt.size / 256.0; + txt.size = 128.0; + } else { + txt.scale = 0.5; + } + iarg += 2; + } else if (strcmp(arg[iarg], "fontcolor") == 0) { + if (iarg + 2 > narg) + utils::missing_cmd_args(FLERR, "fix graphics/labels text fontcolor", error); + try { + get_color(arg[iarg + 1], txt.fontcolor); + } catch (TokenizerException &e) { + error->all(FLERR, iarg + 1, "Error parsing RGB font color value {}: {}", arg[iarg + 1], + e.what()); + } + iarg += 2; + } else if (strcmp(arg[iarg], "backcolor") == 0) { + if (iarg + 2 > narg) + utils::missing_cmd_args(FLERR, "fix graphics/labels text backcolor", error); + try { + get_color(arg[iarg + 1], txt.backcolor); + } catch (TokenizerException &e) { + error->all(FLERR, iarg + 1, "Error parsing RGB font color value {}: {}", arg[iarg + 1], + e.what()); + } + iarg += 2; + } else if (strcmp(arg[iarg], "framecolor") == 0) { + if (iarg + 2 > narg) + utils::missing_cmd_args(FLERR, "fix graphics/labels text framecolor", error); + try { + get_color(arg[iarg + 1], txt.framecolor); + } catch (TokenizerException &e) { + error->all(FLERR, iarg + 1, "Error parsing RGB font color value {}: {}", arg[iarg + 1], + e.what()); + } + iarg += 2; + } else if (strcmp(arg[iarg], "transcolor") == 0) { + if (iarg + 2 > narg) + utils::missing_cmd_args(FLERR, "fix graphics/labels text transcolor", error); + if (strcmp(arg[iarg + 1], "none") == 0) { + txt.notrans = true; + } else { + try { + get_color(arg[iarg + 1], txt.transcolor); + } catch (TokenizerException &e) { + error->all(FLERR, iarg + 1, "Error parsing RGB font color value {}: {}", + arg[iarg + 1], e.what()); + } + } + iarg += 2; + } else { + error->all(FLERR, iarg, "Unknown fix graphics/labels text keyword: {}", arg[iarg]); + } + } + texts.emplace_back(txt); + } else { + error->all(FLERR, iarg, "Unknown fix graphics/labels keyword: {}", arg[iarg]); + } + } +} + +/* ---------------------------------------------------------------------- */ + +FixGraphicsLabels::~FixGraphicsLabels() +{ + for (auto &pix : pixmaps) { + delete[] pix.pixmap; + delete[] pix.xstr; + delete[] pix.ystr; + delete[] pix.zstr; + delete[] pix.sstr; + } + + for (auto &txt : texts) { + delete[] txt.pixmap; + delete[] txt.xstr; + delete[] txt.ystr; + delete[] txt.zstr; + delete[] txt.sstr; + } + + memory->destroy(imgobjs); + memory->destroy(imgparms); +} + +/* ---------------------------------------------------------------------- */ + +int FixGraphicsLabels::setmask() +{ + return END_OF_STEP; +} + +/* ---------------------------------------------------------------------- */ + +void FixGraphicsLabels::init() +{ + for (auto &pix : pixmaps) { + if (pix.xstr) { + int ivar = input->variable->find(pix.xstr); + if (ivar < 0) + error->all(FLERR, Error::NOLASTLINE, + "Variable name {} for fix graphics/labels does not exist", pix.xstr); + if (input->variable->equalstyle(ivar) == 0) + error->all(FLERR, Error::NOLASTLINE, + "fix graphics/labels variable {} is not equal-style variable", pix.xstr); + pix.xvar = ivar; + } + if (pix.ystr) { + int ivar = input->variable->find(pix.ystr); + if (ivar < 0) + error->all(FLERR, Error::NOLASTLINE, + "Variable name {} for fix graphics/labels does not exist", pix.ystr); + if (input->variable->equalstyle(ivar) == 0) + error->all(FLERR, Error::NOLASTLINE, + "fix graphics/labels variable {} is not equal-style variable", pix.ystr); + pix.yvar = ivar; + } + if (pix.zstr) { + int ivar = input->variable->find(pix.zstr); + if (ivar < 0) + error->all(FLERR, Error::NOLASTLINE, + "Variable name {} for fix graphics/labels does not exist", pix.zstr); + if (input->variable->equalstyle(ivar) == 0) + error->all(FLERR, Error::NOLASTLINE, + "fix graphics/labels variable {} is not equal-style variable", pix.zstr); + pix.zvar = ivar; + } + if (pix.sstr) { + int ivar = input->variable->find(pix.sstr); + if (ivar < 0) + error->all(FLERR, Error::NOLASTLINE, + "Variable name {} for fix graphics/labels does not exist", pix.sstr); + if (input->variable->equalstyle(ivar) == 0) + error->all(FLERR, Error::NOLASTLINE, + "fix graphics/labels variable {} is not equal-style variable", pix.sstr); + pix.svar = ivar; + } + } + + for (auto &txt : texts) { + if (txt.xstr) { + int ivar = input->variable->find(txt.xstr); + if (ivar < 0) + error->all(FLERR, Error::NOLASTLINE, + "Variable name {} for fix graphics/labels does not exist", txt.xstr); + if (input->variable->equalstyle(ivar) == 0) + error->all(FLERR, Error::NOLASTLINE, + "fix graphics/labels variable {} is not equal-style variable", txt.xstr); + txt.xvar = ivar; + } + if (txt.ystr) { + int ivar = input->variable->find(txt.ystr); + if (ivar < 0) + error->all(FLERR, Error::NOLASTLINE, + "Variable name {} for fix graphics/labels does not exist", txt.ystr); + if (input->variable->equalstyle(ivar) == 0) + error->all(FLERR, Error::NOLASTLINE, + "fix graphics/labels variable {} is not equal-style variable", txt.ystr); + txt.yvar = ivar; + } + if (txt.zstr) { + int ivar = input->variable->find(txt.zstr); + if (ivar < 0) + error->all(FLERR, Error::NOLASTLINE, + "Variable name {} for fix graphics/labels does not exist", txt.zstr); + if (input->variable->equalstyle(ivar) == 0) + error->all(FLERR, Error::NOLASTLINE, + "fix graphics/labels variable {} is not equal-style variable", txt.zstr); + txt.zvar = ivar; + } + if (txt.sstr) { + int ivar = input->variable->find(txt.sstr); + if (ivar < 0) + error->all(FLERR, Error::NOLASTLINE, + "Variable name {} for fix graphics/labels does not exist", txt.sstr); + if (input->variable->equalstyle(ivar) == 0) + error->all(FLERR, Error::NOLASTLINE, + "fix graphics/labels variable {} is not equal-style variable", txt.sstr); + txt.svar = ivar; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixGraphicsLabels::setup(int) +{ + end_of_step(); +} + +/* ---------------------------------------------------------------------- */ + +void FixGraphicsLabels::end_of_step() +{ + numobjs = pixmaps.size() + texts.size(); + if (numobjs == 0) return; + + if (varflag) modify->clearstep_compute(); + + memory->destroy(imgobjs); + memory->destroy(imgparms); + memory->create(imgobjs, numobjs, "fix_graphics_labels:imgobjs"); + memory->create(imgparms, numobjs, 11, "fix_graphics_labels:imgparms"); + + int n = 0; + for (auto &pix : pixmaps) { + if (pix.xstr) pix.pos[0] = input->variable->compute_equal(pix.xvar); + if (pix.ystr) pix.pos[1] = input->variable->compute_equal(pix.yvar); + if (pix.zstr) pix.pos[2] = input->variable->compute_equal(pix.zvar); + if (pix.sstr) pix.scale = input->variable->compute_equal(pix.svar); + + // if image file has been changed since, free old data and re-read file + + if (comm->me == 0) { + auto timestamp = platform::file_write_time(pix.filename); + if (pix.timestamp != timestamp) { + pix.timestamp = timestamp; + + FILE *fp = fopen(pix.filename.c_str(), "rb"); + if (!fp) + error->one(FLERR, Error::NOLASTLINE, "Cannot open fix graphics/labels image file {}: {}", + pix.filename, utils::getsyserror()); + std::string info; + pix.pixmap = read_image(fp, pix.width, pix.height, pix.filename, info); + fclose(fp); + if (!pix.pixmap) + error->one(FLERR, Error::NOLASTLINE, + "Reading fix graphics/labels image file {} failed: {}", pix.filename, info); + + utils::logmesg(lmp, "Re-read image from {} file: {} format\n", pix.filename, info); + } + } + + imgobjs[n] = Graphics::PIXMAP; + imgparms[n][0] = 1; + imgparms[n][1] = pix.pos[0]; + imgparms[n][2] = pix.pos[1]; + imgparms[n][3] = pix.pos[2]; + imgparms[n][4] = pix.width; + imgparms[n][5] = pix.height; + imgparms[n][6] = ubuf((int64_t) pix.pixmap).d; + imgparms[n][7] = pix.transcolor[0] / 255.0; + imgparms[n][8] = pix.transcolor[1] / 255.0; + imgparms[n][9] = pix.transcolor[2] / 255.0; + imgparms[n][10] = pix.scale; + ++n; + } + + // initialize font renderer and load in-memory font + + try { + SSFN::ScalableFont renderfont; + + for (auto &txt : texts) { + if (txt.xstr) txt.pos[0] = input->variable->compute_equal(txt.xvar); + if (txt.ystr) txt.pos[1] = input->variable->compute_equal(txt.yvar); + if (txt.zstr) txt.pos[2] = input->variable->compute_equal(txt.zvar); + + // text is rasterized at twice the size for some anti-aliasing. clamp to avoid crashes. + if (txt.sstr) { + txt.size = 2.0 * input->variable->compute_equal(txt.svar); + if (txt.size > 128.0) { + txt.scale = txt.size / 256.0; + txt.size = 128.0; + } else { + txt.size = MAX(txt.size, 8.0); + txt.scale = 0.5; + } + } + + renderfont.select_font(SSFN::FAMILY_SANS, SSFN::STYLE_REGULAR, (int) txt.size); + + // need to render the pixmap if NULL, the size is a variable, or we need to substitute the text + if (txt.sstr || !txt.pixmap || (txt.text.find('$') != std::string::npos)) { + auto expanded = txt.text; + + // substitute variables in text + if (expanded.find('$') != std::string::npos) { + int ncopy = expanded.length() + 1; + int nwork = ncopy; + char *copy = (char *) memory->smalloc(ncopy * sizeof(char), "fix/graphics/labels:copy"); + char *work = (char *) memory->smalloc(nwork * sizeof(char), "fix/graphics/labels:work"); + strncpy(copy, expanded.c_str(), ncopy); + input->substitute(copy, work, ncopy, nwork, 0); + expanded = copy; + memory->sfree(copy); + memory->sfree(work); + } + + delete[] txt.pixmap; + txt.pixmap = renderfont.create_pixmap(expanded, txt.width, txt.height, txt.fontcolor, + txt.framecolor, txt.backcolor); + } + imgobjs[n] = Graphics::PIXMAP; + imgparms[n][0] = 1; + imgparms[n][1] = txt.pos[0]; + imgparms[n][2] = txt.pos[1]; + imgparms[n][3] = txt.pos[2]; + imgparms[n][4] = txt.width; + imgparms[n][5] = txt.height; + imgparms[n][6] = ubuf((int64_t) txt.pixmap).d; + if (txt.notrans) { + imgparms[n][7] = imgparms[n][8] = imgparms[n][9] = -1.0; + } else { + imgparms[n][7] = (double)txt.transcolor[0] / 255.0; + imgparms[n][8] = (double)txt.transcolor[1] / 255.0; + imgparms[n][9] = (double)txt.transcolor[2] / 255.0; + } + + imgparms[n][10] = txt.scale; + ++n; + } + } catch (const SSFN::SSFNException &e) { + error->all(FLERR, Error::NOLASTLINE, "Error during font rendering: {}", e.what()); + } + + if (varflag) modify->addstep_compute((update->ntimestep / nevery) * nevery + nevery); +} + +/* ---------------------------------------------------------------------- + provide graphics information to dump image +------------------------------------------------------------------------- */ + +int FixGraphicsLabels::image(int *&objs, double **&parms) +{ + if (comm->me == 0) { + objs = imgobjs; + parms = imgparms; + return numobjs; + } + return 0; +} diff --git a/src/GRAPHICS/fix_graphics_labels.h b/src/GRAPHICS/fix_graphics_labels.h new file mode 100644 index 00000000000..6143367fbbe --- /dev/null +++ b/src/GRAPHICS/fix_graphics_labels.h @@ -0,0 +1,80 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef FIX_CLASS +// clang-format off +FixStyle(graphics/labels,FixGraphicsLabels); +// clang-format on +#else + +#ifndef LMP_FIX_GRAPHICS_LABELS_H +#define LMP_FIX_GRAPHICS_LABELS_H + +#include "fix.h" + +namespace LAMMPS_NS { +class Compute; +class ComputeChunkAtom; + +class FixGraphicsLabels : public Fix { + public: + FixGraphicsLabels(class LAMMPS *, int, char **); + ~FixGraphicsLabels() override; + int setmask() override; + void init() override; + void setup(int) override; + void end_of_step() override; + + int image(int *&, double **&) override; + + protected: + struct PixmapInfo { + std::string filename; + double timestamp; + double pos[3]; + int width; + int height; + unsigned char *pixmap; + double transcolor[3]; + double scale; + int xvar, yvar, zvar, svar; + char *xstr, *ystr, *zstr, *sstr; + }; + std::vector pixmaps; + + struct TextInfo { + std::string text; + double pos[3]; + int width; + int height; + unsigned char *pixmap; + unsigned char fontcolor[3]; + unsigned char backcolor[3]; + unsigned char framecolor[3]; + unsigned char transcolor[3]; + bool notrans; + double size; + double scale; + int xvar, yvar, zvar, svar; + char *xstr, *ystr, *zstr, *sstr; + }; + std::vector texts; + + int varflag; + int numobjs; + int *imgobjs; + double **imgparms; +}; +} // namespace LAMMPS_NS +#endif +#endif diff --git a/src/GRAPHICS/fix_graphics_objects.cpp b/src/GRAPHICS/fix_graphics_objects.cpp new file mode 100644 index 00000000000..b560c48ef85 --- /dev/null +++ b/src/GRAPHICS/fix_graphics_objects.cpp @@ -0,0 +1,514 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "fix_graphics_objects.h" + +#include "comm.h" +#include "domain.h" +#include "error.h" +#include "graphics.h" +#include "input.h" +#include "lattice.h" +#include "math_extra.h" +#include "memory.h" +#include "modify.h" +#include "update.h" +#include "variable.h" + +#include +#include + +using namespace LAMMPS_NS; +using namespace FixConst; + +enum { SPHERE, CYLINDER, ARROW, CONE, PROGBAR }; +enum { X = 0, Y, Z }; + +/* ---------------------------------------------------------------------- */ + +#define PARSE_VARIABLE(value, name, index) \ + if (strstr(arg[index], "v_") == arg[index]) { \ + varflag = 1; \ + name = utils::strdup(arg[index] + 2); \ + } else \ + value = utils::numeric(FLERR, arg[index], false, lmp) + +FixGraphicsObjects::FixGraphicsObjects(LAMMPS *lmp, int narg, char **arg) : + Fix(lmp, narg, arg), imgobjs(nullptr), imgparms(nullptr) +{ + if (narg < 4) utils::missing_cmd_args(FLERR, "fix graphics/objects", error); + + // parse mandatory arg + + nevery = utils::inumeric(FLERR, arg[3], false, lmp); + if (nevery <= 0) error->all(FLERR, 3, "Illegal fix graphics/objects nevery value {}", nevery); + global_freq = nevery; + dynamic_group_allow = 1; + + numobjs = 0; + varflag = 0; + + int iarg = 4; + while (iarg < narg) { + if (strcmp(arg[iarg], "sphere") == 0) { + if (iarg + 6 > narg) utils::missing_cmd_args(FLERR, "fix graphics/objects sphere", error); + // clang-format off + SphereItem sphere{SPHERE, 1, {0.0, 0.0, 0.0}, 0.0, nullptr, nullptr, nullptr, nullptr, + -1, -1, -1, -1}; + // clang-format on + sphere.type = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); + PARSE_VARIABLE(sphere.pos[X], sphere.xstr, iarg + 2); + PARSE_VARIABLE(sphere.pos[Y], sphere.ystr, iarg + 3); + PARSE_VARIABLE(sphere.pos[Z], sphere.zstr, iarg + 4); + PARSE_VARIABLE(sphere.diameter, sphere.dstr, iarg + 5); + sphere.diameter *= 2.0; + items.emplace_back(sphere); + ++numobjs; + iarg += 6; + } else if (strcmp(arg[iarg], "cylinder") == 0) { + if (iarg + 9 > narg) utils::missing_cmd_args(FLERR, "fix graphics/objects cylinder", error); + // clang-format off + CylinderItem cylinder{CYLINDER, 1, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, 0.0, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + -1, -1, -1, -1, -1, -1, -1}; + // clang-format on + cylinder.type = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); + PARSE_VARIABLE(cylinder.pos1[X], cylinder.x1str, iarg + 2); + PARSE_VARIABLE(cylinder.pos1[Y], cylinder.y1str, iarg + 3); + PARSE_VARIABLE(cylinder.pos1[Z], cylinder.z1str, iarg + 4); + PARSE_VARIABLE(cylinder.pos2[X], cylinder.x2str, iarg + 5); + PARSE_VARIABLE(cylinder.pos2[Y], cylinder.y2str, iarg + 6); + PARSE_VARIABLE(cylinder.pos2[Z], cylinder.z2str, iarg + 7); + PARSE_VARIABLE(cylinder.diameter, cylinder.dstr, iarg + 8); + cylinder.diameter *= 2.0; + items.emplace_back(cylinder); + ++numobjs; + iarg += 9; + } else if (strcmp(arg[iarg], "arrow") == 0) { + if (iarg + 10 > narg) utils::missing_cmd_args(FLERR, "fix graphics/objects arrow", error); + // clang-format off + ArrowItem arrow{ARROW, 1, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, 0.0, 0.1, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + -1, -1, -1, -1, -1, -1, -1}; + // clang-format on + arrow.type = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); + PARSE_VARIABLE(arrow.bot[X], arrow.x1str, iarg + 2); + PARSE_VARIABLE(arrow.bot[Y], arrow.y1str, iarg + 3); + PARSE_VARIABLE(arrow.bot[Z], arrow.z1str, iarg + 4); + PARSE_VARIABLE(arrow.tip[X], arrow.x2str, iarg + 5); + PARSE_VARIABLE(arrow.tip[Y], arrow.y2str, iarg + 6); + PARSE_VARIABLE(arrow.tip[Z], arrow.z2str, iarg + 7); + PARSE_VARIABLE(arrow.diameter, arrow.dstr, iarg + 8); + arrow.diameter *= 2.0; + arrow.ratio = utils::numeric(FLERR, arg[iarg + 9], false, lmp); + if ((arrow.ratio < 0.1) || (arrow.ratio > 0.5)) + error->all(FLERR, iarg + 9, "Arrow tip ratio must be between 0.1 and 0.5"); + items.emplace_back(arrow); + ++numobjs; + iarg += 10; + } else if (strcmp(arg[iarg], "cone") == 0) { + if (iarg + 11 > narg) utils::missing_cmd_args(FLERR, "fix graphics/objects cone", error); + // clang-format off + ConeItem cone{CONE, 1, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, 0.0, 0.0, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + -1, -1, -1, -1, -1, -1, -1, -1, Graphics::CONE_ALL}; + // clang-format on + cone.type = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); + PARSE_VARIABLE(cone.bot[X], cone.x1str, iarg + 2); + PARSE_VARIABLE(cone.bot[Y], cone.y1str, iarg + 3); + PARSE_VARIABLE(cone.bot[Z], cone.z1str, iarg + 4); + PARSE_VARIABLE(cone.top[X], cone.x2str, iarg + 5); + PARSE_VARIABLE(cone.top[Y], cone.y2str, iarg + 6); + PARSE_VARIABLE(cone.top[Z], cone.z2str, iarg + 7); + PARSE_VARIABLE(cone.botdiam, cone.d1str, iarg + 8); + PARSE_VARIABLE(cone.topdiam, cone.d2str, iarg + 9); + cone.botdiam *= 2.0; + cone.topdiam *= 2.0; + cone.sides = utils::inumeric(FLERR, arg[iarg + 10], false, lmp); + if ((cone.sides < 0) || (cone.sides > 7)) + error->all(FLERR, iarg + 10, "Cone sides value must be between 0 and 7"); + items.emplace_back(cone); + ++numobjs; + iarg += 11; + } else if (strcmp(arg[iarg], "progbar") == 0) { + if (iarg + 11 > narg) utils::missing_cmd_args(FLERR, "fix graphics/objects progbar", error); + // clang-format off + ProgbarItem progbar{PROGBAR, 1, 2, Y, 0, {0.0, 0.0, 0.0}, 0.0, 0.0, 0.0, nullptr, -1}; + // clang-format on + progbar.type1 = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); + progbar.type2 = utils::inumeric(FLERR, arg[iarg + 2], false, lmp); + if (strcmp(arg[iarg + 3], "x") == 0) { + progbar.dim = X; + } else if (strcmp(arg[iarg + 3], "y") == 0) { + progbar.dim = Y; + } else if (strcmp(arg[iarg + 3], "z") == 0) { + progbar.dim = Z; + } else { + error->all(FLERR, iarg + 3, "Unsupported progress bar dimension string {}", arg[iarg + 3]); + } + progbar.pos[X] = utils::numeric(FLERR, arg[iarg + 4], false, lmp); + progbar.pos[Y] = utils::numeric(FLERR, arg[iarg + 5], false, lmp); + progbar.pos[Z] = utils::numeric(FLERR, arg[iarg + 6], false, lmp); + progbar.length = utils::numeric(FLERR, arg[iarg + 7], false, lmp); + if ((progbar.length <= 0.0) || (progbar.length > 2.0 * domain->prd[progbar.dim])) + error->all(FLERR, iarg + 7, "Illegal progress bar length {}", arg[iarg + 7]); + progbar.diameter = 2.0 * utils::numeric(FLERR, arg[iarg + 8], false, lmp); + PARSE_VARIABLE(progbar.progress, progbar.pstr, iarg + 9); + progbar.tics = utils::inumeric(FLERR, arg[iarg + 10], false, lmp); + if ((progbar.tics < 0) || (progbar.tics > 20)) + error->all(FLERR, iarg + 10, "Unsupported number of progress bar tics {}", arg[iarg + 10]); + items.emplace_back(progbar); + numobjs += 2 + progbar.tics; + iarg += 11; + } else { + error->all(FLERR, iarg, "Unknown fix graphics/objects keyword {}", arg[iarg]); + } + } + memory->create(imgobjs, numobjs, "fix_graphics/objects:imgobjs"); + memory->create(imgparms, numobjs, 10, "fix_graphics/objects:imgparms"); +} +#undef PARSE_VARIABLE +/* ---------------------------------------------------------------------- */ + +FixGraphicsObjects::~FixGraphicsObjects() +{ + for (auto &gi : items) { + switch (gi.style) { + case SPHERE: + delete[] gi.sphere.xstr; + delete[] gi.sphere.ystr; + delete[] gi.sphere.zstr; + delete[] gi.sphere.dstr; + break; + case CYLINDER: + delete[] gi.cylinder.x1str; + delete[] gi.cylinder.y1str; + delete[] gi.cylinder.z1str; + delete[] gi.cylinder.x2str; + delete[] gi.cylinder.y2str; + delete[] gi.cylinder.z2str; + delete[] gi.cylinder.dstr; + break; + case ARROW: + delete[] gi.arrow.x1str; + delete[] gi.arrow.y1str; + delete[] gi.arrow.z1str; + delete[] gi.arrow.x2str; + delete[] gi.arrow.y2str; + delete[] gi.arrow.z2str; + delete[] gi.arrow.dstr; + break; + case CONE: + delete[] gi.cone.x1str; + delete[] gi.cone.y1str; + delete[] gi.cone.z1str; + delete[] gi.cone.x2str; + delete[] gi.cone.y2str; + delete[] gi.cone.z2str; + delete[] gi.cone.d1str; + delete[] gi.cone.d2str; + break; + case PROGBAR: + delete[] gi.progbar.pstr; + break; + default:; // do nothing + break; + } + } + + memory->destroy(imgobjs); + memory->destroy(imgparms); +} + +/* ---------------------------------------------------------------------- */ + +int FixGraphicsObjects::setmask() +{ + return END_OF_STEP; +} + +/* ---------------------------------------------------------------------- */ + +#define CHECK_VARIABLE(index, name) \ + if (name) { \ + int ivar = input->variable->find(name); \ + if (ivar < 0) \ + error->all(FLERR, Error::NOLASTLINE, \ + "Variable name {} for fix graphics/objects does not exist", name); \ + if (input->variable->equalstyle(ivar) == 0) \ + error->all(FLERR, Error::NOLASTLINE, \ + "Fix graphics/objects variable {} is not equal-style variable", name); \ + index = ivar; \ + } + +void FixGraphicsObjects::init() +{ + int n = 0; + for (auto &gi : items) { + if (gi.style == SPHERE) { + imgobjs[n] = Graphics::SPHERE; + imgparms[n][0] = gi.sphere.type; + CHECK_VARIABLE(gi.sphere.xvar, gi.sphere.xstr); + CHECK_VARIABLE(gi.sphere.yvar, gi.sphere.ystr); + CHECK_VARIABLE(gi.sphere.zvar, gi.sphere.zstr); + CHECK_VARIABLE(gi.sphere.dvar, gi.sphere.dstr); + ++n; + } else if (gi.style == CYLINDER) { + imgobjs[n] = Graphics::CYLINDER; + imgparms[n][0] = gi.cylinder.type; + CHECK_VARIABLE(gi.cylinder.x1var, gi.cylinder.x1str); + CHECK_VARIABLE(gi.cylinder.y1var, gi.cylinder.y1str); + CHECK_VARIABLE(gi.cylinder.z1var, gi.cylinder.z1str); + CHECK_VARIABLE(gi.cylinder.x2var, gi.cylinder.x1str); + CHECK_VARIABLE(gi.cylinder.y2var, gi.cylinder.y1str); + CHECK_VARIABLE(gi.cylinder.z2var, gi.cylinder.z1str); + CHECK_VARIABLE(gi.cylinder.dvar, gi.cylinder.dstr); + ++n; + } else if (gi.style == ARROW) { + imgobjs[n] = Graphics::ARROW; + imgparms[n][0] = gi.arrow.type; + CHECK_VARIABLE(gi.arrow.x1var, gi.arrow.x1str); + CHECK_VARIABLE(gi.arrow.y1var, gi.arrow.y1str); + CHECK_VARIABLE(gi.arrow.z1var, gi.arrow.z1str); + CHECK_VARIABLE(gi.arrow.x2var, gi.arrow.x2str); + CHECK_VARIABLE(gi.arrow.y2var, gi.arrow.y2str); + CHECK_VARIABLE(gi.arrow.z2var, gi.arrow.z2str); + CHECK_VARIABLE(gi.arrow.dvar, gi.arrow.dstr); + imgparms[n][9] = gi.arrow.ratio; + ++n; + } else if (gi.style == CONE) { + imgobjs[n] = Graphics::CONE; + imgparms[n][0] = gi.cone.type; + CHECK_VARIABLE(gi.cone.x1var, gi.cone.x1str); + CHECK_VARIABLE(gi.cone.y1var, gi.cone.y1str); + CHECK_VARIABLE(gi.cone.z1var, gi.cone.z1str); + CHECK_VARIABLE(gi.cone.x2var, gi.cone.x2str); + CHECK_VARIABLE(gi.cone.y2var, gi.cone.y2str); + CHECK_VARIABLE(gi.cone.z2var, gi.cone.z2str); + CHECK_VARIABLE(gi.cone.d1var, gi.cone.d1str); + CHECK_VARIABLE(gi.cone.d2var, gi.cone.d2str); + ++n; + } else if (gi.style == PROGBAR) { + imgobjs[n] = Graphics::CYLINDER; + imgparms[n][0] = gi.progbar.type1; + imgparms[n][1] = gi.progbar.pos[X]; + imgparms[n][2] = gi.progbar.pos[Y]; + imgparms[n][3] = gi.progbar.pos[Z]; + imgparms[n][4] = gi.progbar.pos[X]; + imgparms[n][5] = gi.progbar.pos[Y]; + imgparms[n][6] = gi.progbar.pos[Z]; + imgparms[n][7] = gi.progbar.diameter; + switch (gi.progbar.dim) { + case X: + imgparms[n][1] -= 0.5 * gi.progbar.length; + imgparms[n][4] += 0.5 * gi.progbar.length; + break; + case Y: + imgparms[n][2] -= 0.5 * gi.progbar.length; + imgparms[n][5] += 0.5 * gi.progbar.length; + break; + case Z: + imgparms[n][3] -= 0.5 * gi.progbar.length; + imgparms[n][6] += 0.5 * gi.progbar.length; + break; + default:; // do nothing + } + ++n; + imgobjs[n] = Graphics::CYLINDER; + imgparms[n][0] = gi.progbar.type2; + imgparms[n][1] = gi.progbar.pos[X]; + imgparms[n][2] = gi.progbar.pos[Y]; + imgparms[n][3] = gi.progbar.pos[Z]; + imgparms[n][4] = gi.progbar.pos[X]; + imgparms[n][5] = gi.progbar.pos[Y]; + imgparms[n][6] = gi.progbar.pos[Z]; + imgparms[n][7] = 0.75 * gi.progbar.diameter; + switch (gi.progbar.dim) { + case X: + imgparms[n][1] -= 0.5 * gi.progbar.length; + imgparms[n][4] -= 0.5 * gi.progbar.length; + imgparms[n][3] += 0.2 * gi.progbar.diameter; + imgparms[n][6] += 0.2 * gi.progbar.diameter; + break; + case Y: + imgparms[n][2] -= 0.5 * gi.progbar.length; + imgparms[n][5] -= 0.5 * gi.progbar.length; + imgparms[n][1] += 0.15 * gi.progbar.diameter; + imgparms[n][4] += 0.15 * gi.progbar.diameter; + break; + case Z: + imgparms[n][3] -= 0.5 * gi.progbar.length; + imgparms[n][6] -= 0.5 * gi.progbar.length; + imgparms[n][1] += 0.15 * gi.progbar.diameter; + imgparms[n][4] += 0.15 * gi.progbar.diameter; + break; + default: + break; + } + ++n; + double delta = gi.progbar.length / (double) (gi.progbar.tics - 1); + double lo = gi.progbar.pos[gi.progbar.dim] - 0.5 * gi.progbar.length; + for (int i = 0; i < gi.progbar.tics; ++i) { + imgobjs[n] = Graphics::CYLINDER; + imgparms[n][0] = gi.progbar.type1; + imgparms[n][1] = gi.progbar.pos[X]; + imgparms[n][2] = gi.progbar.pos[Y]; + imgparms[n][3] = gi.progbar.pos[Z]; + imgparms[n][4] = gi.progbar.pos[X]; + imgparms[n][5] = gi.progbar.pos[Y]; + imgparms[n][6] = gi.progbar.pos[Z]; + imgparms[n][7] = 1.1 * gi.progbar.diameter; + switch (gi.progbar.dim) { + case X: + imgparms[n][1] = lo + delta * i - 0.05 * delta; + imgparms[n][4] = lo + delta * i + 0.05 * delta; + break; + case Y: + imgparms[n][2] = lo + delta * i - 0.05 * delta; + imgparms[n][5] = lo + delta * i + 0.05 * delta; + break; + case Z: + imgparms[n][3] = lo + delta * i - 0.05 * delta; + imgparms[n][6] = lo + delta * i + 0.05 * delta; + break; + } + ++n; + } + CHECK_VARIABLE(gi.progbar.pvar, gi.progbar.pstr); + } + } + end_of_step(); +} + +#undef CHECK_VARIABLE +/* ---------------------------------------------------------------------- */ + +void FixGraphicsObjects::end_of_step() +{ + // evaluate variable if necessary, wrap with clear/add + + if (varflag) modify->clearstep_compute(); + + int n = 0; + for (auto &gi : items) { + if (gi.style == SPHERE) { + if (gi.sphere.xstr) gi.sphere.pos[X] = input->variable->compute_equal(gi.sphere.xvar); + if (gi.sphere.ystr) gi.sphere.pos[Y] = input->variable->compute_equal(gi.sphere.yvar); + if (gi.sphere.zstr) gi.sphere.pos[Z] = input->variable->compute_equal(gi.sphere.zvar); + if (gi.sphere.dstr) gi.sphere.diameter = 2.0 * input->variable->compute_equal(gi.sphere.dvar); + imgparms[n][1] = gi.sphere.pos[X]; + imgparms[n][2] = gi.sphere.pos[Y]; + imgparms[n][3] = gi.sphere.pos[Z]; + imgparms[n][4] = gi.sphere.diameter; + ++n; + } else if (gi.style == CYLINDER) { + if (gi.cylinder.x1str) + gi.cylinder.pos1[X] = input->variable->compute_equal(gi.cylinder.x1var); + if (gi.cylinder.y1str) + gi.cylinder.pos1[Y] = input->variable->compute_equal(gi.cylinder.y1var); + if (gi.cylinder.z1str) + gi.cylinder.pos1[Z] = input->variable->compute_equal(gi.cylinder.z1var); + if (gi.cylinder.x2str) + gi.cylinder.pos2[X] = input->variable->compute_equal(gi.cylinder.x2var); + if (gi.cylinder.y2str) + gi.cylinder.pos2[Y] = input->variable->compute_equal(gi.cylinder.y2var); + if (gi.cylinder.z2str) + gi.cylinder.pos2[Z] = input->variable->compute_equal(gi.cylinder.z2var); + if (gi.cylinder.dstr) + gi.cylinder.diameter = 2.0 * input->variable->compute_equal(gi.cylinder.dvar); + imgparms[n][1] = gi.cylinder.pos1[X]; + imgparms[n][2] = gi.cylinder.pos1[Y]; + imgparms[n][3] = gi.cylinder.pos1[Z]; + imgparms[n][4] = gi.cylinder.pos2[X]; + imgparms[n][5] = gi.cylinder.pos2[Y]; + imgparms[n][6] = gi.cylinder.pos2[Z]; + imgparms[n][7] = gi.cylinder.diameter; + ++n; + } else if (gi.style == ARROW) { + if (gi.arrow.x1str) gi.arrow.bot[X] = input->variable->compute_equal(gi.arrow.x1var); + if (gi.arrow.y1str) gi.arrow.bot[Y] = input->variable->compute_equal(gi.arrow.y1var); + if (gi.arrow.z1str) gi.arrow.bot[Z] = input->variable->compute_equal(gi.arrow.z1var); + if (gi.arrow.x2str) gi.arrow.tip[X] = input->variable->compute_equal(gi.arrow.x2var); + if (gi.arrow.y2str) gi.arrow.tip[Y] = input->variable->compute_equal(gi.arrow.y2var); + if (gi.arrow.z2str) gi.arrow.tip[Z] = input->variable->compute_equal(gi.arrow.z2var); + if (gi.arrow.dstr) gi.arrow.diameter = 2.0 * input->variable->compute_equal(gi.arrow.dvar); + + double mid[3], vec[3]; + MathExtra::add3(gi.arrow.tip, gi.arrow.bot, vec); + MathExtra::scale3(0.5, vec, mid); + MathExtra::sub3(gi.arrow.tip, gi.arrow.bot, vec); + imgparms[n][1] = mid[X]; + imgparms[n][2] = mid[Y]; + imgparms[n][3] = mid[Z]; + imgparms[n][7] = MathExtra::len3(vec); + MathExtra::norm3(vec); + imgparms[n][4] = vec[X]; + imgparms[n][5] = vec[Y]; + imgparms[n][6] = vec[Z]; + imgparms[n][8] = gi.arrow.diameter; + ++n; + } else if (gi.style == CONE) { + if (gi.cone.x1str) gi.cone.bot[X] = input->variable->compute_equal(gi.cone.x1var); + if (gi.cone.y1str) gi.cone.bot[Y] = input->variable->compute_equal(gi.cone.y1var); + if (gi.cone.z1str) gi.cone.bot[Z] = input->variable->compute_equal(gi.cone.z1var); + if (gi.cone.x2str) gi.cone.top[X] = input->variable->compute_equal(gi.cone.x2var); + if (gi.cone.y2str) gi.cone.top[Y] = input->variable->compute_equal(gi.cone.y2var); + if (gi.cone.z2str) gi.cone.top[Z] = input->variable->compute_equal(gi.cone.z2var); + if (gi.cone.d1str) gi.cone.botdiam = 2.0 * input->variable->compute_equal(gi.cone.d1var); + if (gi.cone.d2str) gi.cone.topdiam = 2.0 * input->variable->compute_equal(gi.cone.d2var); + + imgparms[n][1] = gi.cone.bot[X]; + imgparms[n][2] = gi.cone.bot[Y]; + imgparms[n][3] = gi.cone.bot[Z]; + imgparms[n][4] = gi.cone.top[X]; + imgparms[n][5] = gi.cone.top[Y]; + imgparms[n][6] = gi.cone.top[Z]; + imgparms[n][7] = gi.cone.botdiam; + imgparms[n][8] = gi.cone.topdiam; + imgparms[n][9] = gi.cone.sides; + ++n; + } else if (gi.style == PROGBAR) { + ++n; + if (gi.progbar.pstr) gi.progbar.progress = input->variable->compute_equal(gi.progbar.pvar); + // bracket into (0.0;1.0] rather than throwing an error for just a viz item + gi.progbar.progress = std::max(std::min(gi.progbar.progress, 1.0), 1.0e-10); + switch (gi.progbar.dim) { + case X: + imgparms[n][1] = gi.progbar.pos[X] + (gi.progbar.progress - 0.5) * gi.progbar.length; + break; + case Y: + imgparms[n][2] = gi.progbar.pos[Y] + (gi.progbar.progress - 0.5) * gi.progbar.length; + break; + case Z: + imgparms[n][3] = gi.progbar.pos[Z] + (gi.progbar.progress - 0.5) * gi.progbar.length; + break; + default: + break; + } + ++n; + n += gi.progbar.tics; + } + } + if (varflag) modify->addstep_compute((update->ntimestep / nevery) * nevery + nevery); +} + +/* ---------------------------------------------------------------------- + provide graphics information to dump image +------------------------------------------------------------------------- */ + +int FixGraphicsObjects::image(int *&objs, double **&parms) +{ + objs = imgobjs; + parms = imgparms; + return numobjs; +} diff --git a/src/fix_graphics.h b/src/GRAPHICS/fix_graphics_objects.h similarity index 79% rename from src/fix_graphics.h rename to src/GRAPHICS/fix_graphics_objects.h index 0ec79ebdc21..4a212b6ba2a 100644 --- a/src/fix_graphics.h +++ b/src/GRAPHICS/fix_graphics_objects.h @@ -13,21 +13,21 @@ #ifdef FIX_CLASS // clang-format off -FixStyle(graphics,FixGraphics); +FixStyle(graphics/objects,FixGraphicsObjects); // clang-format on #else -#ifndef LMP_FIX_GRAPHICS_H -#define LMP_FIX_GRAPHICS_H +#ifndef LMP_FIX_GRAPHICS_OBJECTS_H +#define LMP_FIX_GRAPHICS_OBJECTS_H #include "fix.h" namespace LAMMPS_NS { -class FixGraphics : public Fix { +class FixGraphicsObjects : public Fix { public: - FixGraphics(class LAMMPS *, int, char **); - ~FixGraphics() override; + FixGraphicsObjects(class LAMMPS *, int, char **); + ~FixGraphicsObjects() override; int setmask() override; void init() override; void end_of_step() override; @@ -70,6 +70,18 @@ class FixGraphics : public Fix { int x1var, y1var, z1var, x2var, y2var, z2var, dvar; }; + struct ConeItem { + int style; + int type; + double top[3]; + double bot[3]; + double topdiam; + double botdiam; + char *x1str, *y1str, *z1str, *x2str, *y2str, *z2str, *d1str, *d2str; + int x1var, y1var, z1var, x2var, y2var, z2var, d1var, d2var; + int sides; + }; + struct ProgbarItem { int style; int type1; @@ -89,19 +101,19 @@ class FixGraphics : public Fix { GraphicsItem(const SphereItem &s) : sphere(s) {} GraphicsItem(const CylinderItem &c) : cylinder(c) {} GraphicsItem(const ArrowItem &a) : arrow(a) {} + GraphicsItem(const ConeItem &c) : cone(c) {} GraphicsItem(const ProgbarItem &p) : progbar(p) {} int style; SphereItem sphere; CylinderItem cylinder; ArrowItem arrow; + ConeItem cone; ProgbarItem progbar; }; std::vector items; }; - } // namespace LAMMPS_NS - #endif #endif diff --git a/src/GRAPHICS/fix_graphics_periodic.cpp b/src/GRAPHICS/fix_graphics_periodic.cpp new file mode 100644 index 00000000000..f331e1e8575 --- /dev/null +++ b/src/GRAPHICS/fix_graphics_periodic.cpp @@ -0,0 +1,219 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "fix_graphics_periodic.h" + +#include "atom.h" +#include "comm.h" +#include "domain.h" +#include "error.h" +#include "graphics.h" +#include "group.h" +#include "memory.h" +#include "modify.h" +#include "respa.h" +#include "update.h" + +#include + +using namespace LAMMPS_NS; +using namespace FixConst; + +/* ---------------------------------------------------------------------- */ + +FixGraphicsPeriodic::FixGraphicsPeriodic(LAMMPS *lmp, int narg, char **arg) : + Fix(lmp, narg, arg), imgobjs(nullptr), imgparms(nullptr) +{ + if (narg < 4) utils::missing_cmd_args(FLERR, "fix graphics/periodic", error); + + // parse mandatory arg + + nevery = utils::inumeric(FLERR, arg[3], false, lmp); + if (nevery <= 0) error->all(FLERR, 3, "Illegal fix graphics/periodic nevery value"); + global_freq = nevery; + dynamic_group_allow = 1; + + // initialize and set defaults + atomflag = true; + bondflag = (atom->molecular == Atom::MOLECULAR); + numobjs = 0; + radius = -1.0; + pxlo = pxhi = pylo = pyhi = pzlo = pzhi = 0; + + int iarg = 4; + while (iarg < narg) { + if (strcmp(arg[iarg], "xlo") == 0) { + pxlo = 1; + ++iarg; + } else if (strcmp(arg[iarg], "xhi") == 0) { + pxhi = 1; + ++iarg; + } else if (strcmp(arg[iarg], "ylo") == 0) { + pylo = 1; + ++iarg; + } else if (strcmp(arg[iarg], "yhi") == 0) { + pyhi = 1; + ++iarg; + } else if (strcmp(arg[iarg], "zlo") == 0) { + pzlo = 1; + ++iarg; + } else if (strcmp(arg[iarg], "zhi") == 0) { + pzhi = 1; + ++iarg; + } else if (strcmp(arg[iarg], "radius") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix graphics/periodic radius", error); + if (strcmp(arg[iarg + 1], "auto") == 0) { + radius = -1.0; + } else { + radius = utils::numeric(FLERR, arg[iarg + 1], false, lmp); + if (radius <= 0.0) error->all(FLERR, iarg, "Fix graphics/periodic radius must be > 0"); + } + iarg += 2; + } else if (strcmp(arg[iarg], "atoms") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix graphics/periodic atoms", error); + atomflag = utils::logical(FLERR, arg[iarg + 1], false, lmp); + iarg += 2; + } else if (strcmp(arg[iarg], "bonds") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix graphics/periodic bonds", error); + bondflag = utils::logical(FLERR, arg[iarg + 1], false, lmp); + iarg += 2; + } else { + error->all(FLERR, iarg, "Unknown fix graphics/periodic keyword: {}", arg[iarg]); + } + } + + // error checks + if (bondflag && (atom->molecular != Atom::MOLECULAR)) + error->all(FLERR, Error::NOLASTLINE, + "Cannot display periodic images of bonds with non-molecular system"); +} + +/* ---------------------------------------------------------------------- */ + +FixGraphicsPeriodic::~FixGraphicsPeriodic() +{ + memory->destroy(imgobjs); + memory->destroy(imgparms); +} + +/* ---------------------------------------------------------------------- */ + +int FixGraphicsPeriodic::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + mask |= END_OF_STEP; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixGraphicsPeriodic::setup(int) +{ + end_of_step(); +} + +/* ---------------------------------------------------------------------- */ + +void FixGraphicsPeriodic::end_of_step() +{ + // determine number of replica needed + int nx = 1 + pxlo + pxhi; + int ny = 1 + pylo + pyhi; + int nz = 1 + pzlo + pzhi; + int nrep = nx * ny * nz - 1; + + const auto *const *const x = atom->x; + const auto *const mask = atom->mask; + const auto *const type = atom->type; + const auto *const num_bond = atom->num_bond; + const auto *const *const bond_atom = atom->bond_atom; + + const auto nlocal = atom->nlocal; + const auto *const prd = domain->prd; + + // count number of replica objects needed + int n = 0; + for (int i = 0; i < nlocal; ++i) { + if (mask[i] & groupbit) { + if (atomflag) { ++n; } + if (bondflag) { + for (int j = 0; j < num_bond[i]; ++j) { + int m = atom->map(bond_atom[i][j]); + m = domain->closest_image(i, m); + if (m < 0) continue; + if (mask[m] & groupbit) ++n; + } + } + } + } + + numobjs = n * nrep; + memory->destroy(imgobjs); + memory->destroy(imgparms); + memory->create(imgobjs, numobjs, "fix_graphics_periodic:imgobjs"); + memory->create(imgparms, numobjs, 8, "fix_graphics_periodic:imgparms"); + + n = 0; + for (int i = 0; i < nlocal; ++i) { + if (mask[i] & groupbit) { + for (int ix = pxlo ? -1 : 0; ix <= pxhi; ++ix) { + for (int iy = pylo ? -1 : 0; iy <= pyhi; ++iy) { + for (int iz = pzlo ? -1 : 0; iz <= pzhi; ++iz) { + if ((ix == 0) && (iy == 0) && (iz == 0)) continue; + if (atomflag) { + imgobjs[n] = Graphics::SPHERE; + imgparms[n][0] = type[i]; + imgparms[n][1] = x[i][0] + ix * prd[0]; + imgparms[n][2] = x[i][1] + iy * prd[1]; + imgparms[n][3] = x[i][2] + iz * prd[2]; + imgparms[n][4] = 2.0 * radius; + ++n; + } + if (bondflag) { + for (int j = 0; j < num_bond[i]; ++j) { + int m = atom->map(bond_atom[i][j]); + m = domain->closest_image(i, m); + if (m < 0) continue; + if (mask[m] & groupbit) { + imgobjs[n] = Graphics::BOND; + imgparms[n][0] = type[i]; + imgparms[n][1] = type[m]; + imgparms[n][2] = x[i][0] + ix * prd[0]; + imgparms[n][3] = x[i][1] + iy * prd[1]; + imgparms[n][4] = x[i][2] + iz * prd[2]; + imgparms[n][5] = x[m][0] + ix * prd[0]; + imgparms[n][6] = x[m][1] + iy * prd[1]; + imgparms[n][7] = x[m][2] + iz * prd[2]; + ++n; + } + } + } + } + } + } + } + } +} + +/* ---------------------------------------------------------------------- + provide graphics information to dump image +------------------------------------------------------------------------- */ + +int FixGraphicsPeriodic::image(int *&objs, double **&parms) +{ + objs = imgobjs; + parms = imgparms; + return numobjs; +} diff --git a/src/GRAPHICS/fix_graphics_periodic.h b/src/GRAPHICS/fix_graphics_periodic.h new file mode 100644 index 00000000000..7fbcf986745 --- /dev/null +++ b/src/GRAPHICS/fix_graphics_periodic.h @@ -0,0 +1,50 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef FIX_CLASS +// clang-format off +FixStyle(graphics/periodic,FixGraphicsPeriodic); +// clang-format on +#else + +#ifndef LMP_FIX_GRAPHICS_PERIODIC_H +#define LMP_FIX_GRAPHICS_PERIODIC_H + +#include "fix.h" + +namespace LAMMPS_NS { +class Compute; +class ComputeChunkAtom; + +class FixGraphicsPeriodic : public Fix { + public: + FixGraphicsPeriodic(class LAMMPS *, int, char **); + ~FixGraphicsPeriodic() override; + int setmask() override; + void setup(int) override; + void end_of_step() override; + + int image(int *&, double **&) override; + + protected: + bool atomflag, bondflag; + int pxlo, pxhi, pylo, pyhi, pzlo, pzhi; + double radius; + + int numobjs; + int *imgobjs; + double **imgparms; +}; +} // namespace LAMMPS_NS +#endif +#endif diff --git a/src/image.cpp b/src/GRAPHICS/image.cpp similarity index 78% rename from src/image.cpp rename to src/GRAPHICS/image.cpp index c8ad2d351fb..7695e63a711 100644 --- a/src/image.cpp +++ b/src/GRAPHICS/image.cpp @@ -18,11 +18,14 @@ #include "image.h" +#include "domain.h" #include "error.h" +#include "image_objects.h" #include "math_const.h" #include "math_extra.h" #include "memory.h" #include "random_mars.h" +#include "version.h" #include #include @@ -36,7 +39,6 @@ #include #include #include -#include "version.h" #endif using namespace LAMMPS_NS; @@ -149,9 +151,174 @@ constexpr double transthresh[TRANK][TRANK] = { {0.666015625, 0.416015625, 0.603515625, 0.353515625, 0.650390625, 0.400390625, 0.587890625, 0.337890625, 0.662109375, 0.412109375, 0.599609375, 0.349609375, 0.646484375, 0.396484375, 0.583984375, 0.333984375}}; -} // namespace + +// function to apply bilinear scaling to pixmap +void scale_pixmap(int ow, int oh, const unsigned char *opix, int nw, int nh, unsigned char *npix) +{ + double x_ratio = (double) (ow - 1) / nw; + double y_ratio = (double) (oh - 1) / nh; + + for (int i = 0; i < nh; i++) { + for (int j = 0; j < nw; j++) { + int x = (int) (x_ratio * j); + int y = (int) (y_ratio * i); + double x_diff = (x_ratio * j) - x; + double y_diff = (y_ratio * i) - y; + + // Get the four neighboring pixels in the original pixmap + int offs = y * 3 * ow + 3 * x; + unsigned char a[3] = {opix[offs], opix[offs + 1], opix[offs + 2]}; + offs = y * 3 * ow + 3 * (x + 1); + unsigned char b[3] = {opix[offs], opix[offs + 1], opix[offs + 2]}; + offs = (y + 1) * 3 * ow + 3 * x; + unsigned char c[3] = {opix[offs], opix[offs + 1], opix[offs + 2]}; + offs = (y + 1) * 3 * ow + 3 * (x + 1); + unsigned char d[3] = {opix[offs], opix[offs + 1], opix[offs + 2]}; + + // interpolate R, G, and B channels separately with bilinear scaling + npix[i * 3 * nw + 3 * j] = + (unsigned char) (a[0] * (1 - x_diff) * (1 - y_diff) + b[0] * (x_diff) * (1 - y_diff) + + c[0] * (y_diff) * (1 - x_diff) + d[0] * (x_diff * y_diff)); + + npix[i * 3 * nw + 3 * j + 1] = + (unsigned char) (a[1] * (1 - x_diff) * (1 - y_diff) + b[1] * (x_diff) * (1 - y_diff) + + c[1] * (y_diff) * (1 - x_diff) + d[1] * (x_diff * y_diff)); + + npix[i * 3 * nw + 3 * j + 2] = + (unsigned char) (a[2] * (1 - x_diff) * (1 - y_diff) + b[2] * (x_diff) * (1 - y_diff) + + c[2] * (y_diff) * (1 - x_diff) + d[2] * (x_diff * y_diff)); + } + } +} + +// convert an RGB color to YUV colorspace +void rgb2yuv(int *rgb, int *yuv) +{ + yuv[0] = static_cast((0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2])); + yuv[1] = static_cast(-(0.14713 * rgb[0]) - (0.28886 * rgb[1]) + (0.436 * rgb[2])); + yuv[2] = static_cast((0.615 * rgb[0]) - (0.51499 * rgb[1]) - (0.10001 * rgb[2])); +} + +// convert XPM-like bitmap to 8-bit pixmap +void xpm2pix(int width, int height, const char *xpm, unsigned char *pix, double *fg, double *bg) +{ + for (int j = height; j > 0; --j) { + for (int i = 0; i < width; ++i) { + double *color = (xpm[(j - 1) * width + i] == '#') ? fg : bg; + int idx = (height - j) * width * 3 + 3 * i; + pix[idx] = static_cast(color[0] * 255.0); + pix[idx + 1] = static_cast(color[1] * 255.0); + pix[idx + 2] = static_cast(color[2] * 255.0); + } + } +} // clang-format off +// the following are 32x32 pixel XPM-like bitmaps of the letters X, Y, and Z for the axis labels. +constexpr char letter_x[] = { + " " + " " + " ##### ##### " + " ##### ##### " + " ##### ##### " + " ##### ##### " + " ##### ##### " + " ##### ##### " + " ##### ##### " + " ##### ##### " + " ##### ##### " + " ##### ##### " + " ######### " + " ####### " + " ####### " + " ##### " + " ###### " + " ####### " + " ######## " + " ########## " + " ##### ##### " + " ##### #### " + " #### ##### " + " ##### ##### " + " ##### ##### " + " #### ##### " + " ##### ##### " + " ##### ##### " + " #### ##### " + " ##### ##### " + " ##### ##### " + " "}; + +constexpr char letter_y[] = { + " " + " " + " ##### ##### " + " ##### ##### " + " #### #### " + " ##### ##### " + " ##### ##### " + " #### #### " + " ##### ##### " + " ##### ##### " + " #### #### " + " ##### ##### " + " ########## " + " ######## " + " ######## " + " ###### " + " #### " + " #### " + " #### " + " #### " + " #### " + " #### " + " #### " + " #### " + " #### " + " #### " + " #### " + " #### " + " #### " + " #### " + " #### " + " "}; + +constexpr char letter_z[] = { + " " + " " + " ####################### " + " ####################### " + " ####################### " + " ###### " + " ##### " + " ##### " + " ##### " + " ###### " + " ##### " + " ##### " + " ##### " + " ##### " + " ###### " + " ##### " + " ##### " + " ##### " + " ##### " + " ##### " + " ##### " + " ##### " + " ##### " + " ###### " + " ##### " + " ##### " + " ##### " + " ##### " + " ######################## " + " ######################## " + " ######################## " + " "}; + +} // namespace + /* ---------------------------------------------------------------------- */ Image::Image(LAMMPS *lmp, int nmap_caller) : @@ -168,7 +335,6 @@ Image::Image(LAMMPS *lmp, int nmap_caller) : theta = 60.0 * DEG2RAD; phi = 30.0 * DEG2RAD; zoom = 1.0; - persp = 0.0; shiny = 1.0; ssao = NO; fsaa = NO; @@ -181,7 +347,8 @@ Image::Image(LAMMPS *lmp, int nmap_caller) : ncolors = 0; boxcolor = color2rgb("yellow"); - background[0] = background[1] = background[2] = 0; + background[0] = background[1] = background[2] = 0.0; + background2[0] = background2[1] = background2[2] = -1.0; // define nmap colormaps, all with default settings @@ -290,7 +457,7 @@ void Image::view_params(double boxxlo, double boxxhi, double boxylo, // sufficient to handle common cases where theta = 0 or 180 is degenerate? double dot = MathExtra::dot3(up,camDir); - if (fabs(dot) > 1.0-EPSILON) { + if (fabs(dot) > (1.0 - EPSILON)) { if (theta == 0.0) { camDir[0] = sin(EPSILON)*cos(phi); camDir[1] = sin(EPSILON)*sin(phi); @@ -377,22 +544,39 @@ void Image::view_params(double boxxlo, double boxxhi, double boxylo, /* ---------------------------------------------------------------------- initialize image to background color and depth buffer no need to init surfaceBuffer, since will be based on depth + create background gradient, if background2[0] is >= 0 + otherwise use single background color ------------------------------------------------------------------------- */ void Image::clear() { - int red = background[0]; + int red = background[0]; int green = background[1]; - int blue = background[2]; - - int ix,iy; - for (iy = 0; iy < height; iy ++) - for (ix = 0; ix < width; ix ++) { - imageBuffer[iy * width * 3 + ix * 3 + 0] = red; - imageBuffer[iy * width * 3 + ix * 3 + 1] = green; - imageBuffer[iy * width * 3 + ix * 3 + 2] = blue; - depthBuffer[iy * width + ix] = -1; + int blue = background[2]; + + if (background2[0] < 0.0) { + for (int iy = 0; iy < height; iy ++) { + for (int ix = 0; ix < width; ix ++) { + imageBuffer[iy * width * 3 + ix * 3 + 0] = red; + imageBuffer[iy * width * 3 + ix * 3 + 1] = green; + imageBuffer[iy * width * 3 + ix * 3 + 2] = blue; + depthBuffer[iy * width + ix] = -1; + } } + } else { + for (int iy = 0; iy < height; iy ++) { + double fraction = (double) iy / (double) height; + red = static_cast(fraction * background2[0] + (1.0 - fraction) * background[0]); + green = static_cast(fraction * background2[1] + (1.0 - fraction) * background[1]); + blue = static_cast(fraction * background2[2] + (1.0 - fraction) * background[2]); + for (int ix = 0; ix < width; ix ++) { + imageBuffer[iy * width * 3 + ix * 3 + 0] = red; + imageBuffer[iy * width * 3 + ix * 3 + 1] = green; + imageBuffer[iy * width * 3 + ix * 3 + 2] = blue; + depthBuffer[iy * width + ix] = -1; + } + } + } } /* ---------------------------------------------------------------------- @@ -420,8 +604,7 @@ void Image::merge() else MPI_Waitall(2,requests,MPI_STATUS_IGNORE); for (int i = 0; i < npixels; i++) { - if (depthBuffer[i] < 0 || (depthcopy[i] >= 0 && - depthcopy[i] < depthBuffer[i])) { + if (depthBuffer[i] < 0 || (depthcopy[i] >= 0 && depthcopy[i] < depthBuffer[i])) { depthBuffer[i] = depthcopy[i]; imageBuffer[i*3+0] = rgbcopy[i*3+0]; imageBuffer[i*3+1] = rgbcopy[i*3+1]; @@ -508,6 +691,8 @@ void Image::merge() void Image::draw_box(double (*corners)[3], double diameter, double opacity) { + if ((diameter <= 0.0) || (opacity <= 0.0)) return; // nothing to do + draw_cylinder(corners[0],corners[1],boxcolor,diameter,3,opacity); draw_cylinder(corners[2],corners[3],boxcolor,diameter,3,opacity); draw_cylinder(corners[0],corners[2],boxcolor,diameter,3,opacity); @@ -523,15 +708,149 @@ void Image::draw_box(double (*corners)[3], double diameter, double opacity) } /* ---------------------------------------------------------------------- - draw XYZ axes in red/green/blue + draw XYZ axes with X, Y, and Z labels in red/green/blue axes = 4 end points + user arrow image object to draw axes as arrows + + labels are created from 32x32 size bitmaps stored as string in an XPM-like format + convert the bitmap into an RGB pixmap with foreground and transparent background + choose text and background color to be somewhat similar to minimize artifacts from scaling + switch colors from white/silver to black/darkgray depending on the luminance of the background + offset labels in every direction to avoid them being obscured by the arrows + scale letter pixmaps based on the smaller of image width or height ------------------------------------------------------------------------- */ void Image::draw_axes(double (*axes)[3], double diameter, double opacity) { - draw_cylinder(axes[0],axes[1],color2rgb("red"),diameter,3,opacity); - draw_cylinder(axes[0],axes[2],color2rgb("green"),diameter,3,opacity); - draw_cylinder(axes[0],axes[3],color2rgb("blue"),diameter,3,opacity); + if ((diameter <= 0.0) || (opacity <= 0.0)) return; // nothing to do + + // draw arrows + + const double radius = 0.5 * diameter; + draw_sphere(axes[0], color2rgb("gray"), radius, opacity); + ImageObjects::ArrowObj arrow; + arrow.draw(this, color2rgb("red"), axes[0], axes[1], radius, opacity); + arrow.draw(this, color2rgb("green"), axes[0], axes[2], radius, opacity); + if (domain->dimension == 3) + arrow.draw(this, color2rgb("blue"), axes[0], axes[3], radius, opacity); + + // adjust size of labels based on image size, + // with FSAA active, width and height are doubled; adjust the scale factor accordingly + + double scale = static_cast(MIN(width, height)) / 1440.0; + if (fsaa) scale *= 0.5; + + // determine color of labels + + double *fontcolor = color2rgb("white"); + double *backcolor = color2rgb("silver"); + int bgyuv[3]; + rgb2yuv(background, bgyuv); + if (bgyuv[0] > 192) { // switch to black text only for very bright backgrounds + fontcolor = color2rgb("black"); + backcolor = color2rgb("darkgray"); + } + + // convert bitmap of letters to pixmap and scale/draw. + + unsigned char rgbbuffer[32 * 32 * 3]; + double shiftedpos[3]; + constexpr double DIROFFS = 0.05; + xpm2pix(32, 32, letter_x, rgbbuffer, fontcolor, backcolor); + shiftedpos[0] = axes[1][0] + DIROFFS * (axes[1][0] - axes[0][0]); + shiftedpos[1] = axes[1][1] + radius; + shiftedpos[2] = axes[1][2] - radius; // moving in lower z-direction reduces overlap for X + draw_pixmap(shiftedpos, 32, 32, rgbbuffer, backcolor, scale, opacity); + + xpm2pix(32, 32, letter_y, rgbbuffer, fontcolor, backcolor); + shiftedpos[0] = axes[2][0] + radius; + shiftedpos[1] = axes[2][1] + DIROFFS * (axes[2][1] - axes[0][1]); + shiftedpos[2] = axes[2][2] + radius; + draw_pixmap(shiftedpos, 32, 32, rgbbuffer, backcolor, scale, opacity); + + if (domain->dimension == 3) { + xpm2pix(32, 32, letter_z, rgbbuffer, fontcolor, backcolor); + shiftedpos[0] = axes[3][0] + radius; + shiftedpos[1] = axes[3][1] + radius; + shiftedpos[2] = axes[3][2] + DIROFFS * (axes[3][2] - axes[0][2]); + draw_pixmap(shiftedpos, 32, 32, rgbbuffer, backcolor, scale, opacity); + } +} + +/* ---------------------------------------------------------------------- + scale and add pixmap centered at location x into image with depth buffering + background color indicates transparency and pixels in that color are skipped +------------------------------------------------------------------------- */ + +void Image::draw_pixmap(const double *x, int pixwidth, int pixheight, const unsigned char *pixmap, + double *transcolor, double scale, double opacity) +{ + // nothing to do + if (!pixmap || (pixwidth == 0) || (pixheight == 0) || (scale <= 0.0) || (opacity <= 0.0)) return; + + double xlocal[3] = {x[0] - xctr, x[1] - yctr, x[2] - zctr}; + double xmap = MathExtra::dot3(camRight,xlocal); + double ymap = MathExtra::dot3(camUp,xlocal); + double dist = MathExtra::dot3(camPos,camDir) - MathExtra::dot3(xlocal,camDir); + + double pixelWidth = (tanPerPixel > 0) ? tanPerPixel * dist : -tanPerPixel / zoom; + double xf = xmap / pixelWidth; + double yf = ymap / pixelWidth; + int xc = static_cast(xf); + int yc = static_cast(yf); + + // shift 0,0 to screen center (vs lower left) + + xc += width / 2; + yc += height / 2; + + const unsigned char *mypixmap = pixmap; + unsigned char *npixmap = nullptr; + + // adjust scale factor for FSAA and only scale as much as needed. + if (fsaa) scale *= 2.0; + if (scale != 1.0) { + int nwidth = std::lround(scale * pixwidth + 0.5); + int nheight = std::lround(scale * pixheight + 0.5); + npixmap = new unsigned char[3*nwidth*nheight]; + scale_pixmap(pixwidth, pixheight, pixmap, nwidth, nheight, npixmap); + mypixmap = npixmap; + pixwidth = nwidth; + pixheight = nheight; + } + + int ylo = yc; + int xlo = xc; + double normal[3] = {0.0, 0.0, 1.0}; + + for (int j = 0; j < pixheight; ++j) { + for (int i = 0; i < pixwidth; ++i) { + int iy = ylo + j - pixheight/2; + int ix = xlo + i - pixwidth/2; + if (iy < 0 || iy >= height || ix < 0 || ix >= width) continue; + if (((opacity < 1.0) && (transthresh[ix % TRANK][iy % TRANK] > opacity)) || (opacity <= 0.0)) + continue; + + // get color of pixel at x/y position of pixmap + + double pixelcolor[3]; + int offs = 3*j*pixwidth + 3*i; + pixelcolor[0] = (double)mypixmap[offs] / 255.0; + pixelcolor[1] = (double)mypixmap[offs + 1] / 255.0; + pixelcolor[2] = (double)mypixmap[offs + 2] / 255.0; + + // check for transparency color and skip if it matches + // we allow a few steps difference for each channel to account + // for rounding errors and reduce "bleeding" from interpolation + + if ((fabs(pixelcolor[0] - transcolor[0]) < 0.01) && + (fabs(pixelcolor[1] - transcolor[1]) < 0.01) && + (fabs(pixelcolor[2] - transcolor[2]) < 0.01)) continue; + + draw_pixel(ix, iy, dist, normal, pixelcolor); + } + } + delete[] npixmap; } /* ---------------------------------------------------------------------- @@ -539,8 +858,11 @@ void Image::draw_axes(double (*axes)[3], double diameter, double opacity) render pixel by pixel onto image plane with depth buffering ------------------------------------------------------------------------- */ -void Image::draw_sphere(const double *x, const double *surfaceColor, double diameter, double opacity) +void Image::draw_sphere(const double *x, const double *surfaceColor, double diameter, + double opacity) { + if ((diameter <= 0.0) || (opacity <= 0.0)) return; // nothing to do + double xlocal[3]; xlocal[0] = x[0] - xctr; @@ -553,8 +875,7 @@ void Image::draw_sphere(const double *x, const double *surfaceColor, double diam double radius = 0.5*diameter; double radsq = radius*radius; - double pixelWidth = (tanPerPixel > 0) ? tanPerPixel * dist : - -tanPerPixel / zoom; + double pixelWidth = (tanPerPixel > 0) ? tanPerPixel * dist : -tanPerPixel / zoom; double pixelRadiusFull = radius / pixelWidth; int pixelRadius = std::lround(pixelRadiusFull) + 1; @@ -573,7 +894,8 @@ void Image::draw_sphere(const double *x, const double *surfaceColor, double diam for (int iy = yc - pixelRadius; iy <= yc + pixelRadius; iy++) { for (int ix = xc - pixelRadius; ix <= xc + pixelRadius; ix++) { if (iy < 0 || iy >= height || ix < 0 || ix >= width) continue; - if (((opacity < 1.0) && (transthresh[ix % TRANK][iy % TRANK] > opacity)) || (opacity <= 0.0)) continue; + if (((opacity < 1.0) && (transthresh[ix % TRANK][iy % TRANK] > opacity)) || (opacity <= 0.0)) + continue; double surface[3]; surface[1] = ((iy - yc) - height_error) * pixelWidth; @@ -602,6 +924,8 @@ void Image::draw_sphere(const double *x, const double *surfaceColor, double diam void Image::draw_cube(const double *x, const double *surfaceColor, double diameter, double opacity) { + if ((diameter <= 0.0) || (opacity <= 0.0)) return; // nothing to do + double xlocal[3],surface[3]; double normal[3] = {0.0, 0.0, 1.0}; double t = 1.0; @@ -617,8 +941,7 @@ void Image::draw_cube(const double *x, const double *surfaceColor, double diamet double dist = MathExtra::dot3(camPos,camDir) - MathExtra::dot3(xlocal,camDir); double radius = 0.5*diameter; - double pixelWidth = (tanPerPixel > 0) ? tanPerPixel * dist : - -tanPerPixel / zoom; + double pixelWidth = (tanPerPixel > 0) ? tanPerPixel * dist : -tanPerPixel / zoom; double halfWidth = diameter; double pixelHalfWidthFull = halfWidth / pixelWidth; @@ -639,7 +962,8 @@ void Image::draw_cube(const double *x, const double *surfaceColor, double diamet for (int iy = yc - pixelHalfWidth; iy <= yc + pixelHalfWidth; iy ++) { for (int ix = xc - pixelHalfWidth; ix <= xc + pixelHalfWidth; ix ++) { if (iy < 0 || iy >= height || ix < 0 || ix >= width) continue; - if (((opacity < 1.0) && (transthresh[ix % TRANK][iy % TRANK] > opacity)) || (opacity <= 0.0)) continue; + if (((opacity < 1.0) && (transthresh[ix % TRANK][iy % TRANK] > opacity)) || (opacity <= 0.0)) + continue; double sy = ((iy - yc) - height_error) * pixelWidth; double sx = ((ix - xc) - width_error) * pixelWidth; @@ -713,6 +1037,8 @@ void Image::draw_cube(const double *x, const double *surfaceColor, double diamet void Image::draw_cylinder(const double *x, const double *y, const double *surfaceColor, double diameter, int sflag, double opacity) { + if ((diameter <= 0.0) || (opacity <= 0.0)) return; // nothing to do + double mid[3],xaxis[3],yaxis[3],zaxis[3]; double camLDir[3], camLRight[3], camLUp[3]; double zmin, zmax; @@ -734,6 +1060,7 @@ void Image::draw_cylinder(const double *x, const double *y, mid[2] = (y[2] + x[2]) * 0.5 - zctr; double len = MathExtra::len3(zaxis); + if (len == 0.0) return; // nothing left to do MathExtra::scale3(1.0/len,zaxis); len *= 0.5; zmax = len; @@ -743,8 +1070,7 @@ void Image::draw_cylinder(const double *x, const double *y, double ymap = MathExtra::dot3(camUp,mid); double dist = MathExtra::dot3(camPos,camDir) - MathExtra::dot3(mid,camDir); - double pixelWidth = (tanPerPixel > 0) ? tanPerPixel * dist : - -tanPerPixel / zoom; + double pixelWidth = (tanPerPixel > 0) ? tanPerPixel * dist : -tanPerPixel / zoom; double xf = xmap / pixelWidth; double yf = ymap / pixelWidth; @@ -792,7 +1118,8 @@ void Image::draw_cylinder(const double *x, const double *y, for (int iy = yc - pixelHalfHeight; iy <= yc + pixelHalfHeight; iy ++) { for (int ix = xc - pixelHalfWidth; ix <= xc + pixelHalfWidth; ix ++) { if (iy < 0 || iy >= height || ix < 0 || ix >= width) continue; - if (((opacity < 1.0) && (transthresh[ix % TRANK][iy % TRANK] > opacity)) || (opacity <= 0.0)) continue; + if (((opacity < 1.0) && (transthresh[ix % TRANK][iy % TRANK] > opacity)) || (opacity <= 0.0)) + continue; double surface[3], normal[3]; double sy = ((iy - yc) - height_error) * pixelWidth; @@ -826,9 +1153,9 @@ void Image::draw_cylinder(const double *x, const double *y, // in camera space - surface[0] = MathExtra::dot3 (normal, camLRight); - surface[1] = MathExtra::dot3 (normal, camLUp); - surface[2] = MathExtra::dot3 (normal, camLDir); + surface[0] = MathExtra::dot3(normal, camLRight); + surface[1] = MathExtra::dot3(normal, camLUp); + surface[2] = MathExtra::dot3(normal, camLDir); double depth = dist - t; draw_pixel(ix, iy, depth, surface, surfaceColor); @@ -840,9 +1167,11 @@ void Image::draw_cylinder(const double *x, const double *y, draw triangle with 3 corner points x,y,z, surfaceColor ------------------------------------------------------------------------- */ -void Image::draw_triangle(const double *x, const double *y, const double *z, const double *surfaceColor, - const double opacity) +void Image::draw_triangle(const double *x, const double *y, const double *z, + const double *surfaceColor, const double opacity) { + if (opacity <= 0.0) return; // nothing to do + double d1[3], d1len, d2[3], d2len, normal[3], invndotd; double xlocal[3], ylocal[3], zlocal[3]; double surface[3]; @@ -858,20 +1187,23 @@ void Image::draw_triangle(const double *x, const double *y, const double *z, con zlocal[1] = z[1] - yctr; zlocal[2] = z[2] - zctr; - MathExtra::sub3 (xlocal, ylocal, d1); - d1len = MathExtra::len3 (d1); - MathExtra::scale3 (1.0 / d1len, d1); - MathExtra::sub3 (zlocal, ylocal, d2); - d2len = MathExtra::len3 (d2); - MathExtra::scale3 (1.0 / d2len, d2); + MathExtra::sub3(xlocal, ylocal, d1); + d1len = MathExtra::len3(d1); + if (d1len == 0.0) return; // zero length of triangle side + MathExtra::scale3(1.0 / d1len, d1); - MathExtra::cross3 (d1, d2, normal); - MathExtra::norm3 (normal); - invndotd = 1.0 / MathExtra::dot3(normal, camDir); + MathExtra::sub3(zlocal, ylocal, d2); + d2len = MathExtra::len3(d2); + if (d2len == 0.0) return; // zero length of triangle side + MathExtra::scale3(1.0 / d2len, d2); - // invalid triangle (parallel) + MathExtra::cross3(d1, d2, normal); + MathExtra::norm3(normal); + invndotd = MathExtra::dot3(normal, camDir); - if (invndotd == 0) return; + // triangle parallel to camera and thus invisible + if (invndotd == 0.0) return; + invndotd = 1.0 / invndotd; double r[3],u[3]; @@ -892,9 +1224,7 @@ void Image::draw_triangle(const double *x, const double *y, const double *z, con double ymap = MathExtra::dot3(camUp,xlocal); double dist = MathExtra::dot3(camPos,camDir) - MathExtra::dot3(xlocal,camDir); - double pixelWidth = (tanPerPixel > 0) ? tanPerPixel * dist : - -tanPerPixel / zoom; - + double pixelWidth = (tanPerPixel > 0) ? tanPerPixel * dist : -tanPerPixel / zoom; double xf = xmap / pixelWidth; double yf = ymap / pixelWidth; int xc = static_cast(xf); @@ -919,7 +1249,8 @@ void Image::draw_triangle(const double *x, const double *y, const double *z, con for (int iy = yc - pixelDown; iy <= yc + pixelUp; iy ++) { for (int ix = xc - pixelLeft; ix <= xc + pixelRight; ix ++) { if (iy < 0 || iy >= height || ix < 0 || ix >= width) continue; - if (((opacity < 1.0) && (transthresh[ix % TRANK][iy % TRANK] > opacity)) || (opacity <= 0.0)) continue; + if (((opacity < 1.0) && (transthresh[ix % TRANK][iy % TRANK] > opacity)) || (opacity <= 0.0)) + continue; double sy = ((iy - yc) - height_error) * pixelWidth; double sx = ((ix - xc) - width_error) * pixelWidth; @@ -939,34 +1270,32 @@ void Image::draw_triangle(const double *x, const double *y, const double *z, con double s1[3], s2[3], s3[3]; double c1[3], c2[3]; - // for grid cell viz: - // using <= if test can leave single-pixel gaps between 2 tris - // using < if test fixes it + // for grid cell and other triangle meshes: + // there can be single pixel gaps due to rounding + // using <= if test can leave single-pixel gaps between 2 triangles + // using < if test fixes most of them // suggested by Nathan Fabian, Nov 2022 - MathExtra::sub3 (zlocal, xlocal, s1); - MathExtra::sub3 (ylocal, xlocal, s2); - MathExtra::sub3 (p, xlocal, s3); - MathExtra::cross3 (s1, s2, c1); - MathExtra::cross3 (s1, s3, c2); - if (MathExtra::dot3 (c1, c2) < 0) continue; - //if (MathExtra::dot3 (c1, c2) <= 0) continue; - - MathExtra::sub3 (xlocal, ylocal, s1); - MathExtra::sub3 (zlocal, ylocal, s2); - MathExtra::sub3 (p, ylocal, s3); - MathExtra::cross3 (s1, s2, c1); - MathExtra::cross3 (s1, s3, c2); - if (MathExtra::dot3 (c1, c2) < 0) continue; - //if (MathExtra::dot3 (c1, c2) <= 0) continue; - - MathExtra::sub3 (ylocal, zlocal, s1); - MathExtra::sub3 (xlocal, zlocal, s2); - MathExtra::sub3 (p, zlocal, s3); - MathExtra::cross3 (s1, s2, c1); - MathExtra::cross3 (s1, s3, c2); - if (MathExtra::dot3 (c1, c2) < 0) continue; - //if (MathExtra::dot3 (c1, c2) <= 0) continue; + MathExtra::sub3(zlocal, xlocal, s1); + MathExtra::sub3(ylocal, xlocal, s2); + MathExtra::sub3(p, xlocal, s3); + MathExtra::cross3(s1, s2, c1); + MathExtra::cross3(s1, s3, c2); + if (MathExtra::dot3(c1, c2) < 0.0) continue; + + MathExtra::sub3(xlocal, ylocal, s1); + MathExtra::sub3(zlocal, ylocal, s2); + MathExtra::sub3(p, ylocal, s3); + MathExtra::cross3(s1, s2, c1); + MathExtra::cross3(s1, s3, c2); + if (MathExtra::dot3(c1, c2) < 0.0) continue; + + MathExtra::sub3(ylocal, zlocal, s1); + MathExtra::sub3(xlocal, zlocal, s2); + MathExtra::sub3(p, zlocal, s3); + MathExtra::cross3(s1, s2, c1); + MathExtra::cross3(s1, s3, c2); + if (MathExtra::dot3(c1, c2) < 0.0) continue; double cNormal[3]; cNormal[0] = MathExtra::dot3(camRight, normal); @@ -984,9 +1313,10 @@ void Image::draw_triangle(const double *x, const double *y, const double *z, con void Image::draw_pixel(int ix, int iy, double depth, const double *surface, const double *surfaceColor) { + if (!std::isfinite(depth)) return; // reject pixels with invalid depth buffer values + double diffuseKey,diffuseFill,diffuseBack,specularKey; - if (depth < 0 || (depthBuffer[ix + iy*width] >= 0 && - depth >= depthBuffer[ix + iy*width])) return; + if (depth < 0 || (depthBuffer[ix + iy*width] >= 0 && depth >= depthBuffer[ix + iy*width])) return; depthBuffer[ix + iy*width] = depth; // store only the tangent relative to the camera normal (0,0,-1) @@ -1040,8 +1370,7 @@ void Image::compute_SSAO() // typical neighborhood value for shading - double pixelWidth = (tanPerPixel > 0) ? tanPerPixel : - -tanPerPixel / zoom; + double pixelWidth = (tanPerPixel > 0) ? tanPerPixel : -tanPerPixel / zoom; int pixelRadius = (int) trunc (SSAORadius / pixelWidth + 0.5); // each proc is assigned a subset of contiguous pixels from the full image @@ -1065,7 +1394,7 @@ void Image::compute_SSAO() int y = index / width; double cdepth = depthBuffer[index]; - if (cdepth < 0) { continue; } + if (cdepth < 0) continue; double sx = surfaceBuffer[index * 2 + 0]; double sy = surfaceBuffer[index * 2 + 1]; @@ -1220,7 +1549,7 @@ void Image::write_PNG(FILE *fp) return; } - if (setjmp(png_jmpbuf(png_ptr))) { + if (setjmp(png_jmpbuf(png_ptr))) { // NOLINT png_destroy_write_struct(&png_ptr, &info_ptr); return; } @@ -1269,10 +1598,10 @@ void Image::write_PPM(FILE *fp) const int ppmheight = height/aafactor; const int ppmwidth = width/aafactor; - fprintf(fp,"P6\n%d %d\n255\n",ppmwidth,ppmheight); + fprintf(fp,"P6\n%d %d\n",ppmwidth,ppmheight); + fprintf(fp,"# CREATOR: dump image\n# SOFTWARE: LAMMPS version %s\n255\n", LAMMPS_VERSION); - int y; - for (y = ppmheight-1; y >= 0; y--) + for (int y = ppmheight-1; y >= 0; y--) fwrite(&writeBuffer[y*ppmwidth*3],3,ppmwidth,fp); } @@ -1336,8 +1665,9 @@ int Image::addcolor(char *name, double r, double g, double b) username[icolor] = new char[n]; strcpy(username[icolor],name); - if (r < 0.0 || r > 1.0 || g < 0.0 || g > 1.0 || b < 0.0 || b > 1.0) - return 1; + if (r < 0.0 || r > 1.0) return 1; + if (g < 0.0 || g > 1.0) return 2; + if (b < 0.0 || b > 1.0) return 3; userrgb[icolor][0] = r; userrgb[icolor][1] = g; @@ -1703,7 +2033,7 @@ double *Image::element2color(char *element) {0.8, 0.2, 0.2}, {0.7, 0.85, 0.45}, {0.6431372549, 0.6666666667, 0.6784313725}, - {0.6, 0.6, 0.6}, + {0.6, 0.6, 0.8}, {0.6, 0.6, 0.7}, {0.6431372549, 0.6666666667, 0.6784313725}, {0.6901960784, 0.768627451, 0.8705882353}, @@ -1711,7 +2041,7 @@ double *Image::element2color(char *element) {0.95, 0.9, 0.2}, {0.15, 0.5, 0.1}, {0.6431372549, 0.6666666667, 0.6784313725}, - {0.5, 0.5, 0.5}, + {0.8, 0.5, 0.5}, {0.8, 0.8, 0.7}, {0.6431372549, 0.6666666667, 0.6784313725}, {0.6431372549, 0.6666666667, 0.6784313725}, @@ -1887,117 +2217,102 @@ ColorMap::~ColorMap() /* ---------------------------------------------------------------------- redefine color map args = lo hi style delta N entry1 entry2 ... entryN as defined by caller - return 1 if any error in args, else return 0 + return > 0 if any error in args, else return 0 + return value is position of failed arg, i.e. array index+1 ------------------------------------------------------------------------- */ int ColorMap::reset(int narg, char **arg) { - if (!islower(arg[0][0])) { + if (utils::is_double(arg[0])) { mlo = NUMERIC; mlovalue = utils::numeric(FLERR,arg[0],false,lmp); } else if (strcmp(arg[0],"min") == 0) mlo = MINVALUE; else return 1; - if (!islower(arg[1][0])) { + if (utils::is_double(arg[1])) { mhi = NUMERIC; mhivalue = utils::numeric(FLERR,arg[1],false,lmp); } else if (strcmp(arg[1],"max") == 0) mhi = MAXVALUE; - else return 1; + else return 2; - if (mlo == NUMERIC && mhi == NUMERIC && mlovalue >= mhivalue) return 1; + if ((mlo == NUMERIC) && (mhi == NUMERIC) && (mlovalue >= mhivalue)) return 1; - if (mlo == MINVALUE || mhi == MAXVALUE) dynamic = 1; + if ((mlo == MINVALUE) || (mhi == MAXVALUE)) dynamic = 1; else dynamic = 0; - if (strlen(arg[2]) != 2) return 1; + if (strlen(arg[2]) != 2) return 3; if (arg[2][0] == 'c') mstyle = CONTINUOUS; else if (arg[2][0] == 'd') mstyle = DISCRETE; else if (arg[2][0] == 's') mstyle = SEQUENTIAL; - else return 1; + else return 3; if (arg[2][1] == 'a') mrange = ABSOLUTE; else if (arg[2][1] == 'f') mrange = FRACTIONAL; - else return 1; + else return 3; if (mstyle == SEQUENTIAL) { mbinsize = utils::numeric(FLERR,arg[3],false,lmp); - if (mbinsize <= 0.0) return 1; + if (mbinsize <= 0.0) return 4; mbinsizeinv = 1.0/mbinsize; } nentry = utils::inumeric(FLERR,arg[4],false,lmp); - if (nentry < 1) return 1; + if (nentry < 1) return 5; delete [] mentry; mentry = new MapEntry[nentry]; int expandflag = 0; - int n = 5; for (int i = 0; i < nentry; i++) { if (mstyle == CONTINUOUS) { - if (n+2 > narg) return 1; - if (!islower(arg[n][0])) { + if (n+2 > narg) return n; + if (utils::is_double(arg[n])) { mentry[i].single = NUMERIC; mentry[i].svalue = utils::numeric(FLERR,arg[n],false,lmp); } else if (strcmp(arg[n],"min") == 0) mentry[i].single = MINVALUE; else if (strcmp(arg[n],"max") == 0) mentry[i].single = MAXVALUE; - else return 1; + else return n+1; mentry[i].color = image->color2rgb(arg[n+1]); n += 2; } else if (mstyle == DISCRETE) { - if (n+3 > narg) return 1; - if (!islower(arg[n][0])) { + if (n+3 > narg) return n+1; + if (utils::is_double(arg[n])) { mentry[i].lo = NUMERIC; mentry[i].lvalue = utils::numeric(FLERR,arg[n],false,lmp); } else if (strcmp(arg[n],"min") == 0) mentry[i].lo = MINVALUE; else if (strcmp(arg[n],"max") == 0) mentry[i].lo = MAXVALUE; - else return 1; - if (!islower(arg[n+1][0])) { + else return n+1; + if (utils::is_double(arg[n+1])) { mentry[i].hi = NUMERIC; mentry[i].hvalue = utils::numeric(FLERR,arg[n+1],false,lmp); } else if (strcmp(arg[n+1],"min") == 0) mentry[i].hi = MINVALUE; else if (strcmp(arg[n+1],"max") == 0) mentry[i].hi = MAXVALUE; - else return 1; + else return n+2; mentry[i].color = image->color2rgb(arg[n+2]); n += 3; } else if (mstyle == SEQUENTIAL) { - // NOTE: this is unfinished code, not sure how useful it is - // idea is to allow a list of colors to be specified with a single arg - // problem is that sequential colors in ALL are not very different - // e.g. ALL or USER or ALL5:10 or USER1:10:2 - // current code is just 1st nentry values of ALL or USER - // need to comment out error check in DumpImage::modify_param() - // for amap check on (narg < n) to get it to work - // need to add extra logic here to check not accessing undefined colors - if (i == 0) { - if (n+1 > narg) return 1; - if (strcmp(arg[n],"ALL") == 0) expandflag = 1; - if (strcmp(arg[n],"USER") == 0) expandflag = 2; - } - if (expandflag == 0) { - if (n+1 > narg) return 1; - mentry[i].color = image->color2rgb(arg[n]); - } else if (expandflag == 1) { - mentry[i].color = image->color2rgb(nullptr,i+1); - } else if (expandflag == 2) { - mentry[i].color = image->color2rgb(nullptr,-(i+1)); - } + if (n+1 > narg) return n+1; + mentry[i].color = image->color2rgb(arg[n]); n += 1; } - if (mentry[i].color == nullptr) return 1; + if (mentry[i].color == nullptr) return n; } if (mstyle == CONTINUOUS) { - if (nentry < 2) return 1; - if (mentry[0].single != MINVALUE || mentry[nentry-1].single != MAXVALUE) - return 1; + if (nentry < 2) return 5; + if (mentry[0].single != MINVALUE) + return 6; + if (mentry[nentry-1].single != MAXVALUE) + return 4 + nentry*2; for (int i = 2; i < nentry-1; i++) - if (mentry[i].svalue <= mentry[i-1].svalue) return 1; + if (mentry[i].svalue <= mentry[i-1].svalue) return 4 + 2*(i+1); } else if (mstyle == DISCRETE) { - if (nentry < 1) return 1; - if (mentry[nentry-1].lo != MINVALUE || mentry[nentry-1].hi != MAXVALUE) - return 1; + if (nentry < 1) return 5; + if (mentry[nentry-1].lo != MINVALUE) + return 3 + nentry*3; + if (mentry[nentry-1].hi != MAXVALUE) + return 4 + nentry*3; } else if (mstyle == SEQUENTIAL) { - if (nentry < 1) return 1; + if (nentry < 1) return 5; } // one-time call to minmax if color map is static diff --git a/src/image.h b/src/GRAPHICS/image.h similarity index 95% rename from src/image.h rename to src/GRAPHICS/image.h index eecfe29564b..a619a70c1e2 100644 --- a/src/image.h +++ b/src/GRAPHICS/image.h @@ -26,7 +26,6 @@ class Image : protected Pointers { double xctr, yctr, zctr; // center of image in user coords double up[3]; // up direction in image double zoom; // zoom factor - double persp; // perspective factor double shiny; // shininess of objects int fsaa; // antialiasing on or off int ssao; // SSAO on or off @@ -34,6 +33,7 @@ class Image : protected Pointers { double ssaoint; // strength of shading from 0 to 1 double *boxcolor; // color to draw box outline with int background[3]; // RGB values of background + int background2[3]; // RGB values of second background color for gradient (off if < 0.0) double ambientColor[3]; // light color settings (adjustable by caller) double keyLightColor[3]; @@ -58,6 +58,8 @@ class Image : protected Pointers { double opacity = 1.0); void draw_box(double (*)[3], double, double opacity = 1.0); void draw_axes(double (*)[3], double, double opacity = 1.0); + void draw_pixmap(const double *, int, int, const unsigned char *, double *, double scale = 1.0, + double opacity = 1.0); int map_dynamic(int); int map_reset(int, int, char **); @@ -181,7 +183,5 @@ class ColorMap : protected Pointers { MapEntry *mentry; int nentry; }; - } // namespace LAMMPS_NS - #endif diff --git a/src/GRAPHICS/image_objects.cpp b/src/GRAPHICS/image_objects.cpp new file mode 100644 index 00000000000..fac25f1effb --- /dev/null +++ b/src/GRAPHICS/image_objects.cpp @@ -0,0 +1,609 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "image_objects.h" + +#include "image.h" +#include "math_const.h" +#include "math_extra.h" +#include "region.h" + +#include +#include +#include +#include + +using namespace LAMMPS_NS; +using namespace ImageObjects; + +namespace { + +using LAMMPS_NS::MathConst::MY_2PI; +constexpr double RADOVERLAP = 0.01; +constexpr double SMALL = 1.0e-10; + +// helper functions for generating and transforming triangle meshes + +// dot product of two vectors +inline double vec3dot(const vec3 &a, const vec3 &b) +{ + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} + +// cross product of two vectors +inline vec3 vec3cross(const vec3 &a, const vec3 &b) +{ + return {a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]}; +} + +// length of vector +inline double vec3len(const vec3 &v) +{ + return sqrt(vec3dot(v, v)); +} + +// return normalized vector +inline vec3 vec3norm(const vec3 &v) +{ + double n = vec3len(v); + return (n > 0.0) ? (1.0 / n) * v : vec3{0.0, 0.0, 0.0}; +} + +// scale factor to move a position to the surface of an ellipsoid with given shape parameters +inline double radscale(const double *shape, const vec3 &pos) +{ + return sqrt(1.0 / + (pos[0] / shape[0] * pos[0] / shape[0] + pos[1] / shape[1] * pos[1] / shape[1] + + pos[2] / shape[2] * pos[2] / shape[2])); +} + +// re-orient list of triangles to point along "dir", then scale and translate it. +std::vector transform(const std::vector &triangles, const vec3 &dir, + const vec3 &offs, double len, double width) +{ + // customized vector + std::vector newtriangles; + + // normalize direction vector + vec3 u = vec3norm(dir); + + // vector is too short. can't draw anything. return empty list + if (vec3len(u) < SMALL) return newtriangles; + + // construct orthonormal basis around direction vector + vec3 a = (std::fabs(u[0]) < 0.9) ? vec3{1.0, 0.0, 0.0} : vec3{0.0, 1.0, 0.0}; + vec3 v = vec3norm(vec3cross(u, a)); + vec3 w = vec3cross(u, v); + + // now process the template triangles and return the transformed list + newtriangles.reserve(triangles.size()); + for (const auto &tri : triangles) { + vec3 p1 = (len * tri[0][0] * u) + (width * tri[0][1] * v) + (width * tri[0][2] * w) + offs; + vec3 p2 = (len * tri[1][0] * u) + (width * tri[1][1] * v) + (width * tri[1][2] * w) + offs; + vec3 p3 = (len * tri[2][0] * u) + (width * tri[2][1] * v) + (width * tri[2][2] * w) + offs; + newtriangles.push_back({p1, p2, p3}); + } + return newtriangles; +} +} // namespace + +// construct an arrow from primitives, mostly triangles and a cylinder, and draw them + +// construct arrow template by placing sets of triangles with two corners on a circle at "mid" +// and the third corner in the center either at "mid" or at "tip". A third set of triangles +// it at "bot". The resolution parameter determines how many triangles per set (36 by default) +// "bot" to "tip" is 1.0, "tiplength is "mid" to "tip". "diameter" is the width at "bot" +// "tipwidth" is the additional width at "mid". +// +// |\ +// |--------| \ +// |--------| / +// |/ +// ^ ^ ^ +// bot mid tip + +ArrowObj::ArrowObj(double _tipl, double _tipw, double radius, int res) +{ + triangles.clear(); + + // we want at least 2 iterations. + if (res < 2) return; + + // store settings for arrow template + + tiplength = _tipl; + tipwidth = _tipw; + diameter = 2.0 * radius; + resolution = res; + + vec3 tip{0.5, 0.0, 0.0}; + vec3 mid{0.5 - tiplength, 0.0, 0.0}; + vec3 bot{-0.5, 0.0, 0.0}; + + // construct list of triangles for the tip of the arrow. p1, p2 are the points on the "rim". + + const double radinc = MY_2PI / resolution; + vec3 p1{0.5 - tiplength, 0.0, 0.0}; + vec3 p2{0.5 - tiplength, 0.0, 0.0}; + for (int i = 0; i < resolution; ++i) { + p1[1] = (radius + tipwidth) * sin(radinc * i - RADOVERLAP); + p1[2] = (radius + tipwidth) * cos(radinc * i - RADOVERLAP); + p2[1] = (radius + tipwidth) * sin(radinc * (i + 1)); + p2[2] = (radius + tipwidth) * cos(radinc * (i + 1)); + triangles.emplace_back(triangle{p2, tip, p1}); + triangles.emplace_back(triangle{p2, mid, p1}); + } + + // construct list of triangles for the cap at the bottom + + p1[0] = -0.5; + p2[0] = -0.5; + for (int i = 0; i < resolution; ++i) { + p1[1] = radius * sin(radinc * i - RADOVERLAP); + p1[2] = radius * cos(radinc * i - RADOVERLAP); + p2[1] = radius * sin(radinc * (i + 1)); + p2[2] = radius * cos(radinc * (i + 1)); + triangles.emplace_back(triangle{p2, bot, p1}); + } +} + +// draw custom arrow from unit template using center, direction, and length +void ArrowObj::draw(Image *img, const double *color, const double *center, double length, + const double *data, double scale, double opacity) +{ + // nothing to draw + if (!triangles.size()) return; + + // transform the template into the arrow object we want to draw + + vec3 dir{data[0], data[1], data[2]}; + double lscale = vec3len(dir) * length; + double wscale = scale / diameter; + + auto arrow = transform(triangles, dir, {center[0], center[1], center[2]}, lscale, wscale); + + // nothing to draw + if (!arrow.size()) return; + + // draw tip and bottom from list of triangles + for (const auto &tri : arrow) + img->draw_triangle(tri[0].data(), tri[1].data(), tri[2].data(), color, opacity); + + // infer cylinder end points for body from list of triangles + // (middle corner of all triangles in the the second and last set of triangles) + if ((int) arrow.size() > resolution + 2) + img->draw_cylinder(arrow[1][1].data(), arrow[arrow.size() - 1][1].data(), color, scale, 0, + opacity); +} + +// draw custom arrow from unit template using center, direction, and length +void ArrowObj::draw(Image *img, const double *color, const double *bottom, const double *tip, + double scale, double opacity) +{ + // nothing to draw + if (!triangles.size()) return; + + // transform the template into the arrow object we want to draw + + vec3 dir{vec3{tip[0], tip[1], tip[2]} - vec3{bottom[0], bottom[1], bottom[2]}}; + vec3 center{0.5 * dir + vec3{bottom[0], bottom[1], bottom[2]}}; + double lscale = vec3len(dir); + double wscale = scale / diameter; + + auto arrow = transform(triangles, dir, {center[0], center[1], center[2]}, lscale, wscale); + + // nothing to draw + if (!arrow.size()) return; + + // draw tip and bottom from list of triangles + for (const auto &tri : arrow) + img->draw_triangle(tri[0].data(), tri[1].data(), tri[2].data(), color, opacity); + + // infer cylinder end points for body from list of triangles + // (middle corner of all triangles in the the second and last set of triangles) + if ((int) arrow.size() > resolution + 2) + img->draw_cylinder(arrow[1][1].data(), arrow[arrow.size() - 1][1].data(), color, scale, 0, + opacity); +} + +// construct a truncated cone from triangles and draw them + +// we have two circles and place triangles that connect from bottom to top and back +// where the top of the triangle alternates direction. the caps on either end use the +// same circle coordinates but the tip is the center of the object. +// as an optimization we skip triangles where the bottom is on the circle when the +// diameter on either side of the cone is zero. +// a cylinder is just a special case of a cone with both radii of the same value. +// +// |\ +// | \ +// | | _ center +// | | +// | / +// |/ +// ^ ^ +//bot top + +ConeObj::ConeObj(double length, double topwidth, double botwidth, int flag, int resolution) +{ + triangles.clear(); + + // we want at least 2 iterations. + if (resolution < 2) return; + + // store settings for cone + + bool dotop = (flag & Graphics::CONE_TOP) > 0; + bool dobot = (flag & Graphics::CONE_BOT) > 0; + bool doside = (flag & Graphics::CONE_SIDE) > 0; + + vec3 top{0.5 * length, 0.0, 0.0}; + vec3 bot{-0.5 * length, 0.0, 0.0}; + + // construct list of triangles + + const double radinc = MY_2PI / resolution; + vec3 p1top{top}; + vec3 p2top{top}; + vec3 p1bot{bot}; + vec3 p2bot{bot}; + + for (int i = 0; i < resolution; ++i) { + if (topwidth > 0.0) { + p1top[1] = topwidth * sin(radinc * i - RADOVERLAP); + p1top[2] = topwidth * cos(radinc * i - RADOVERLAP); + p2top[1] = topwidth * sin(radinc * (i + 1)); + p2top[2] = topwidth * cos(radinc * (i + 1)); + // cap on top + if (dotop) triangles.emplace_back(triangle{p1top, top, p2top}); + } + if (botwidth > 0.0) { + p1bot[1] = botwidth * sin(radinc * i - RADOVERLAP); + p1bot[2] = botwidth * cos(radinc * i - RADOVERLAP); + p2bot[1] = botwidth * sin(radinc * (i + 1)); + p2bot[2] = botwidth * cos(radinc * (i + 1)); + // cap at bottom + if (dobot) triangles.emplace_back(triangle{p1bot, bot, p2bot}); + } + // side + if (doside) { + if (topwidth > 0.0) triangles.emplace_back(triangle{p1top, p1bot, p2top}); + if (botwidth > 0.0) triangles.emplace_back(triangle{p1bot, p2bot, p2top}); + } + } +} + +// draw triangle mesh for region. flag 1 is triangles, flag 2 is wireframe, flag 3 both + +void ConeObj::draw(Image *img, int flag, const vec3 &dir, const vec3 &mid, const double *color, + Region *reg, double diameter, double opacity) +{ + // nothing to draw + if (!triangles.size()) return; + + // rotate to selected axis and translate from origin to original center + // no need of scaling here since length and width was already applied during construction + auto cone = transform(triangles, dir, mid, 1.0, 1.0); + + // nothing to draw + if (!cone.size()) return; + + int n = 0; + for (auto &tri : cone) { + // apply region rotation and translation + reg->forward_transform(tri[0][0], tri[0][1], tri[0][2]); + reg->forward_transform(tri[1][0], tri[1][1], tri[1][2]); + reg->forward_transform(tri[2][0], tri[2][1], tri[2][2]); + + // draw triangle + if (flag & 1) img->draw_triangle(tri[0].data(), tri[1].data(), tri[2].data(), color, opacity); + + // draw wireframe + if (flag & 2) { + // draw bottom rim and straight lines from bottom to top + img->draw_cylinder(tri[0].data(), tri[1].data(), color, diameter, 3, opacity); + // only draw top rim by picking coordinates from every other triangle + ++n; + if (n & 1) img->draw_cylinder(tri[0].data(), tri[2].data(), color, diameter, 3, opacity); + } + } +} + +// draw triangle mesh for fix. + +void ConeObj::draw(Image *img, const vec3 &bot, const vec3 &top, const double *color, + double opacity) +{ + // nothing to draw + if (!triangles.size()) return; + + vec3 mid{0.5 * (top + bot)}; + vec3 dir{top - bot}; + double length = vec3len(dir); + dir = vec3norm(dir); + + // rotate to selected axis and translate from origin to original center + // no need of scaling here since length and width was already applied during construction + auto cone = transform(triangles, dir, mid, length, 1.0); + + // nothing to draw + if (!cone.size()) return; + + for (auto &tri : cone) { + // draw triangle + img->draw_triangle(tri[0].data(), tri[1].data(), tri[2].data(), color, opacity); + } +} + +/**************************************************************************** + * Refine triangle mesh by replacing each triangle with four triangles. + * Compute the new positions so they are located on a sphere with radius 1. + * /\ /\ + * / \ /_ \ + * / \ --> /\ /\ + * /______\ /__\/__\ + ***************************************************************************/ + +void EllipsoidObj::refine() +{ + std::vector newlist; + for (const auto &tri : triangles) { + vec3 posa = vec3norm(tri[0] + tri[2]); + vec3 posb = vec3norm(tri[0] + tri[1]); + vec3 posc = vec3norm(tri[1] + tri[2]); + newlist.push_back({tri[0], posb, posa}); + newlist.push_back({posb, tri[1], posc}); + newlist.push_back({posa, posb, posc}); + newlist.push_back({posa, posc, tri[2]}); + } + triangles = std::move(newlist); +} + +// Construct and draw an ellipsoid from primitives, triangles and cylinders. +// Build a triangle mesh by refinining the triangles of an octahedron + +EllipsoidObj::EllipsoidObj(int level) +{ + // Define edges of an octahedron to approximate a sphere of radius 1 around the origin. + constexpr vec3 OCT1 = {-1.0, 0.0, 0.0}; + constexpr vec3 OCT2 = {1.0, 0.0, 0.0}; + constexpr vec3 OCT3 = {0.0, -1.0, 0.0}; + constexpr vec3 OCT4 = {0.0, 1.0, 0.0}; + constexpr vec3 OCT5 = {0.0, 0.0, -1.0}; + constexpr vec3 OCT6 = {0.0, 0.0, 1.0}; + + // define level 1 octahedron triangle mesh, normals pointing away from the center. + triangles = {{OCT5, OCT4, OCT1}, {OCT2, OCT4, OCT5}, {OCT6, OCT4, OCT2}, {OCT1, OCT4, OCT6}, + {OCT1, OCT3, OCT5}, {OCT5, OCT3, OCT2}, {OCT2, OCT3, OCT6}, {OCT6, OCT3, OCT1}}; + + // refine the list of triangles to the desired level + for (int i = 1; i < level; ++i) refine(); +} + +// draw method for drawing ellipsoids from a region which has its own transformation function +void EllipsoidObj::draw(Image *img, int flag, const double *color, const double *center, + const double *shape, Region *reg, double diameter, double opacity) +{ + // select between triangles or cylinders + bool doframe = false; + bool dotri = false; + if (flag == 1) dotri = true; + if (flag == 2) doframe = true; + if (diameter <= 0.0) doframe = false; + if (!dotri && !doframe) return; // nothing to do + + // optimization: just draw a sphere if a filled surface is requested and the object is a sphere + if (dotri && (shape[0] == shape[1]) && (shape[0] == shape[2])) { + img->draw_sphere(center, color, 2.0 * shape[0], opacity); + return; + } + + // nothing to draw + if (!triangles.size()) return; + + // draw triangles + + const vec3 offs{center[0], center[1], center[2]}; + for (auto tri : triangles) { + + // set shape and move + tri[0] = tri[0] * radscale(shape, tri[0]) + offs; + reg->forward_transform(tri[0][0], tri[0][1], tri[0][2]); + tri[1] = tri[1] * radscale(shape, tri[1]) + offs; + reg->forward_transform(tri[1][0], tri[1][1], tri[1][2]); + tri[2] = tri[2] * radscale(shape, tri[2]) + offs; + reg->forward_transform(tri[2][0], tri[2][1], tri[2][2]); + + if (dotri) img->draw_triangle(tri[0].data(), tri[1].data(), tri[2].data(), color, opacity); + if (doframe) { + img->draw_cylinder(tri[0].data(), tri[1].data(), color, diameter, 3, opacity); + img->draw_cylinder(tri[0].data(), tri[2].data(), color, diameter, 3, opacity); + img->draw_cylinder(tri[1].data(), tri[2].data(), color, diameter, 3, opacity); + } + } +} + +// draw method for drawing ellipsoids from per-atom data which has a quaternion +// and the shape list to define the orientation and stretch +void EllipsoidObj::draw(Image *img, int flag, const double *color, const double *center, + const double *shape, const double *quat, double diameter, double opacity) +{ + // select between triangles or cylinders or both + bool doframe = true; + bool dotri = true; + if (flag == 1) doframe = false; + if (flag == 2) dotri = false; + if (diameter <= 0.0) doframe = false; + if (!dotri && !doframe) return; // nothing to do + + double p[3][3]; + vec3 e1, e2, e3; + const vec3 offs{center[0], center[1], center[2]}; + + // optimization: just draw a sphere if a filled surface is requested and the object is a sphere + if (dotri && (shape[0] == shape[1]) && (shape[0] == shape[2])) { + img->draw_sphere(center, color, 2.0 * shape[0], opacity); + return; + } + + // nothing to draw + if (!triangles.size()) return; + + // get rotation matrix for body frame to box frame + MathExtra::quat_to_mat(quat, p); + + // draw triangles and edges as requested, work on copy of triangle since we modify it + for (auto tri : triangles) { + + if (dotri) { + // set shape by shifting each corner to the surface + for (int i = 0; i < 3; ++i) { + auto &t = tri[i]; + t = radscale(shape, t) * t; + } + + // rotate + MathExtra::matvec(p, tri[0].data(), e1.data()); + MathExtra::matvec(p, tri[1].data(), e2.data()); + MathExtra::matvec(p, tri[2].data(), e3.data()); + + // translate + e1 = e1 + offs; + e2 = e2 + offs; + e3 = e3 + offs; + + img->draw_triangle(e1.data(), e2.data(), e3.data(), color, opacity); + } + + if (doframe) { + // set shape + for (int i = 0; i < 3; ++i) { + auto &t = tri[i]; + if (dotri) { + // shift the cylinder positions inward by their diameter when using cylinders and + // triangles together for a smoother surface to avoid increasing the final size + double shapeplus[3] = {shape[0] - diameter, shape[1] - diameter, shape[1] - diameter}; + t = radscale(shapeplus, t) * t; + } else { + t = radscale(shape, t) * t; + } + } + + // rotate + MathExtra::matvec(p, tri[0].data(), e1.data()); + MathExtra::matvec(p, tri[1].data(), e2.data()); + MathExtra::matvec(p, tri[2].data(), e3.data()); + + // translate + e1 = e1 + offs; + e2 = e2 + offs; + e3 = e3 + offs; + img->draw_cylinder(e1.data(), e2.data(), color, diameter, 3, opacity); + img->draw_cylinder(e2.data(), e3.data(), color, diameter, 3, opacity); + img->draw_cylinder(e3.data(), e1.data(), color, diameter, 3, opacity); + } + } +} + +/*********************************************************************** + * refine triangle mesh by replacing each triangle with four triangles + * /\ /\ + * / \ /__\ + * / \ --> /\ /\ + * /______\ /__\/__\ +***********************************************************************/ + +void PlaneObj::refine() +{ + std::vector newlist; + for (const auto &tri : triangles) { + vec3 posa = 0.5 * (tri[0] + tri[2]); + vec3 posb = 0.5 * (tri[0] + tri[1]); + vec3 posc = 0.5 * (tri[1] + tri[2]); + newlist.push_back({tri[0], posb, posa}); + newlist.push_back({posb, tri[1], posc}); + newlist.push_back({posa, posb, posc}); + newlist.push_back({posa, posc, tri[2]}); + } + triangles = std::move(newlist); +} + +// construct a plane from many triangles (so we can truncate it to the box dimensions) + +PlaneObj::PlaneObj(int level) +{ + // define edges and center of a square + constexpr vec3 SQ1 = {0.0, 1.0, 1.0}; + constexpr vec3 SQ2 = {0.0, 1.0, -1.0}; + constexpr vec3 SQ3 = {0.0, -1.0, -1.0}; + constexpr vec3 SQ4 = {0.0, -1.0, 1.0}; + constexpr vec3 CEN = {0.0, 0.0, 0.0}; + + // define unit plane with norm (1.0,0.0,0.0) from four triangles + triangles = {{SQ2, CEN, SQ1}, {SQ3, CEN, SQ2}, {SQ4, CEN, SQ3}, {SQ1, CEN, SQ4}}; + + // refine the list of triangles to the desired level + for (int i = 1; i < level; ++i) refine(); +} + +// draw method for drawing planes from a region which has its own transformation function + +void PlaneObj::draw(Image *img, int flag, const double *color, const double *center, + const double *norm, const double *boxlo, const double *boxhi, double scale, + Region *reg, double diameter, double opacity) +{ + // select between triangles or cylinders + bool doframe = false; + bool dotri = false; + if (flag == 1) dotri = true; + if (flag == 2) doframe = true; + if (diameter <= 0.0) doframe = false; + if (!dotri && !doframe) return; // nothing to do + + // nothing to draw + + if (!triangles.size()) return; + + // draw triangles after scaling and shifting the mesh + + const vec3 dir{norm[0], norm[1], norm[2]}; + const vec3 offs{center[0], center[1], center[2]}; + auto plane = transform(triangles, dir, offs, scale, scale); + + for (auto tri : plane) { + + // rotate and translate + + reg->forward_transform(tri[0][0], tri[0][1], tri[0][2]); + reg->forward_transform(tri[1][0], tri[1][1], tri[1][2]); + reg->forward_transform(tri[2][0], tri[2][1], tri[2][2]); + + // skip drawing triangle if all corners are outside the box in one direction + + int n = 0; + for (int i = 0; i < 3; ++i) { + if (((tri[0][i] < boxlo[i]) || (tri[0][i] > boxhi[i])) && + ((tri[1][i] < boxlo[i]) || (tri[1][i] > boxhi[i])) && + ((tri[2][i] < boxlo[i]) || (tri[2][i] > boxhi[i]))) + ++n; + } + if (n) continue; + + if (dotri) img->draw_triangle(tri[0].data(), tri[1].data(), tri[2].data(), color, opacity); + if (doframe) { + img->draw_cylinder(tri[0].data(), tri[1].data(), color, diameter, 3, opacity); + img->draw_cylinder(tri[0].data(), tri[2].data(), color, diameter, 3, opacity); + img->draw_cylinder(tri[1].data(), tri[2].data(), color, diameter, 3, opacity); + } + } +} diff --git a/src/GRAPHICS/image_objects.h b/src/GRAPHICS/image_objects.h new file mode 100644 index 00000000000..ccda1ccabc6 --- /dev/null +++ b/src/GRAPHICS/image_objects.h @@ -0,0 +1,124 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef LMP_IMAGE_OBJECTS_H +#define LMP_IMAGE_OBJECTS_H + +#include "graphics.h" + +#include +#include + +namespace LAMMPS_NS { +class Image; +class Region; +namespace ImageObjects { + constexpr int RESOLUTION = 36; // default resolution for cylindrical objects + constexpr int DEF_ELEVEL = 3; // default refinement level for ellipsoids + constexpr int DEF_PLEVEL = 6; // default refinement level for planes + + // custom data types for positions and triangles based on std::array + using vec3 = std::array; + using triangle = std::array; + + // some basic math operations for positions/vectors + inline vec3 operator+(const vec3 &a, const vec3 &b) + { + return {a[0] + b[0], a[1] + b[1], a[2] + b[2]}; + } + + inline vec3 operator-(const vec3 &a, const vec3 &b) + { + return {a[0] - b[0], a[1] - b[1], a[2] - b[2]}; + } + + inline vec3 operator*(double s, const vec3 &v) + { + return {s * v[0], s * v[1], s * v[2]}; + } + + inline vec3 operator*(const vec3 &v, double s) + { + return s * v; + } + + class ArrowObj { + public: + // build an arrow template with length 1 in (1.0, 0.0, 0.0) direction as list of triangles + ArrowObj(double _tipl = 0.2, double _tipw = 0.1, double radius = 0.1, int res = RESOLUTION); + + // draw custom arrow from unit template using center, direction, and length + void draw(Image *, const double *, const double *, double, const double *, double, double); + // draw custom arrow from unit template using two end points + void draw(Image *, const double *, const double *, const double *, double, double); + + private: + double tiplength; + double tipwidth; + double diameter; + std::vector triangles; + int resolution; + }; + + class ConeObj { + public: + // build a truncated cone in (1.0, 0.0, 0.0) direction centered at (0.0, 0.0, 0.0) + // with given length and top / bottom diameter as list of triangles. + // flag is bitmap deciding whether top / bottom or side is shown. + ConeObj(double, double, double, int flag = Graphics::CONE_ALL, int res = RESOLUTION); + + // draw triangle mesh for region. flag 1 is triangles, flag 2 is wireframe, flag 3 both + void draw(Image *, int, const vec3 &, const vec3 &, const double *, Region *, double, double); + + // draw triangle mesh for fix + void draw(Image *img, const vec3 &, const vec3 &, const double *, double); + + private: + std::vector triangles; + }; + + class EllipsoidObj { + public: + // construct (spherical) triangle mesh by refinining the triangles of an octahedron + EllipsoidObj(int level = DEF_ELEVEL); + + // draw ellipsoid from triangle mesh for ellipsoid particles + void draw(Image *, int, const double *, const double *, const double *, const double *, double, + double opacity = 1.0); + + // draw ellipsoid from triangle mesh for ellipsoid regions + void draw(Image *, int, const double *, const double *, const double *, Region *, double, + double opacity = 1.0); + + private: + std::vector triangles; + void refine(); + }; + + class PlaneObj { + public: + // build a plane template with four triangles extending well outside the box + PlaneObj(int level = DEF_PLEVEL); + + // draw plane for region after transforming it. + void draw(Image *, int, const double *, const double *, const double *, const double *, + const double *, double, Region *reg, double, double opacity = 1.0); + + private: + std::vector triangles; + void refine(); + }; +} // namespace ImageObjects +} // namespace LAMMPS_NS + +#endif diff --git a/src/EXTRA-COMMAND/region2vmd.cpp b/src/GRAPHICS/region2vmd.cpp similarity index 100% rename from src/EXTRA-COMMAND/region2vmd.cpp rename to src/GRAPHICS/region2vmd.cpp diff --git a/src/EXTRA-COMMAND/region2vmd.h b/src/GRAPHICS/region2vmd.h similarity index 100% rename from src/EXTRA-COMMAND/region2vmd.h rename to src/GRAPHICS/region2vmd.h diff --git a/src/GRAPHICS/scalable_font.cpp b/src/GRAPHICS/scalable_font.cpp new file mode 100644 index 00000000000..9fc52f083e6 --- /dev/null +++ b/src/GRAPHICS/scalable_font.cpp @@ -0,0 +1,1155 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +// adapted from ssfn.h +// +// Copyright (C) 2019 bzt (bztsrc@gitlab) +// https://gitlab.com/bztsrc/scalable-font + +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, copy, +// modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +// Scalable Screen Font renderer in a single ANSI C/C++ file + +#include +#include +#include +#include +#include + +#include "fmt/format.h" +#include "scalable_font.h" + +static constexpr int SSFN_DATA_MAX = 65536; + +namespace SSFN { + +using ssfn_font_t = struct _ssfn_font_t { + uint8_t magic[4]; /* SSFN magic bytes */ + uint32_t size; /* total size in bytes */ + uint8_t family; /* font family group */ + uint8_t style; /* font style, zero or OR'd SSFN_STYLE_BOLD and SSFN_STYLE_ITALIC */ + uint8_t quality; /* quality, defines grid size, 0 - 8 */ + uint8_t features; /* feature flags, OR'd SSFN_FEAT_* */ + uint8_t revision; /* format revision, must be zero */ + uint8_t reserved0; /* must be zero */ + uint16_t reserved1; + uint16_t baseline; /* horizontal baseline in grid pixels */ + uint16_t underline; /* position of under line in grid pixels */ + uint16_t bbox_left; /* overall bounding box for all glyphs in grid pixels */ + uint16_t bbox_top; + uint16_t bbox_right; + uint16_t bbox_bottom; + uint32_t fragments_offs; /* offset of fragments table relative to magic */ + uint32_t characters_offs; /* offset of characters tables relative to magic */ + uint32_t kerning_offs; /* kerning table offset relative to magic */ +}; + +/* returned bitmap struct */ +using ssfn_glyph_t = struct _ssfn_glyph_t { + uint8_t mode; /* returned glyph's data format */ + uint8_t baseline; /* baseline of glyph, scaled to size */ + uint8_t w; /* width */ + uint8_t h; /* height */ + uint8_t adv_x; /* advance x */ + uint8_t adv_y; /* advance y */ + uint16_t pitch; /* data buffer bytes per line */ + uint32_t *cmap; /* pointer to color map */ + uint8_t data[SSFN_DATA_MAX]; /* data buffer */ +}; + +/* renderer context */ + +using ssfn_t = struct _ssfn_t { + const ssfn_font_t **fnt[5]; /* font registry */ + const ssfn_font_t *s; /* explicitly selected font */ + const ssfn_font_t *f; /* font selected by best match */ + ssfn_glyph_t *ret; /* glyph to return */ + uint16_t *p; /* outline points */ + uint16_t *r[256]; /* raster for scanlines */ + uint16_t *h; /* auto hinting grid */ + int len[5]; /* number of fonts in registry */ + int mp; /* memory allocated for points */ + int np; /* how many points actually are there */ + int nr[256]; /* number of coordinates in each raster line */ + int family; /* required family */ + int style; /* required style */ + int size; /* required size */ + int mode; /* required mode */ + int g; /* shift value for grid size */ + int m, ix, u, uix, uax, lx, ly, mx, my; /* helper variables */ +}; + +/***** file format *****/ + +/* magic bytes */ +#define SSFN_MAGIC "SSFN" +#define SSFN_COLLECTION "SFNC" +#define SSFN_ENDMAGIC "NFSS" + +/* file format features */ +#define SSFN_FEAT_HASBMAP 1 /* there's at least one bitmap fragment */ +#define SSFN_FEAT_HASCMAP \ + 2 /* there's at least one pixmap fragment or one color command, so a color map too */ +#define SSFN_FEAT_HASHINT 4 /* there's at least one hinting fragment */ +#define SSFN_FEAT_KBIGLKP 8 /* big offsets in kerning look up table */ +#define SSFN_FEAT_KBIGCHR 16 /* big characters in kerning look up tables */ +#define SSFN_FEAT_KBIGCRD 32 /* big coordinates in kerning groups */ +#define SSFN_FEAT_HBIGCRD 64 /* bit coordinates in autohinting fragments */ + +/* contour commands for vector fragments */ +#define SSFN_CONTOUR_MOVE 0 +#define SSFN_CONTOUR_LINE 1 +#define SSFN_CONTOUR_QUAD 2 +#define SSFN_CONTOUR_CUBIC 3 +#define SSFN_CONTOUR_COLOR 4 + +/* bitmap and pixmap fragments and hinting grid info */ +#define SSFN_FRAG_BITMAP 0 +#define SSFN_FRAG_LBITMAP 1 +#define SSFN_FRAG_PIXMAP 2 +#define SSFN_FRAG_HINTING 3 + +/* main SSFN header */ + +/***** renderer API *****/ +#define SSFN_FAMILY_ANY 0xff /* select the first loaded font */ +#define SSFN_FAMILY_BYNAME 0xfe /* select font by its unique name */ + +#define SSFN_STYLE_UNDERLINE 4 /* under line glyph */ +#define SSFN_STYLE_STHROUGH 8 /* strike through glyph */ +#define SSFN_STYLE_NOHINTING 0x40 /* no auto hinting grid */ +#define SSFN_STYLE_ABS_SIZE 0x80 /* use absolute size value */ + +#define SSFN_FRAG_CONTOUR 255 + +/* grid fitting */ +#define SSFN_HINTING_THRESHOLD 16 /* don't change unless you really know what you're doing */ + +/*** normal renderer (ca. 22k, fully featured with error checking) ***/ + +namespace { +/* error codes */ +#define SSFN_OK 0 /* success */ +#define SSFN_ERR_ALLOC 1 /* allocation error */ +#define SSFN_ERR_NOFACE 2 /* no font face selected */ +#define SSFN_ERR_INVINP 3 /* invalid input */ +#define SSFN_ERR_BADFILE 4 /* bad SSFN file format */ +#define SSFN_ERR_BADSTYLE 5 /* bad style */ +#define SSFN_ERR_BADSIZE 6 /* bad size */ +#define SSFN_ERR_BADMODE 7 /* bad mode */ +#define SSFN_ERR_NOGLYPH 8 /* glyph (or kerning info) not found */ + + /** + * Error code strings + */ + const char *ssfn_errstr[] = {"", + "Memory allocation error", + "No font face found", + "Invalid input value", + "Bad file format", + "Invalid style", + "Invalid size", + "Invalid mode", + "Glyph not found"}; + +// include font data as constant in memory byte sequence +#include "scalable_sans_font.h" + const ssfn_font_t *const ssfn_sans_font = (ssfn_font_t *) VeraR_sfn; + + /*** Private functions ***/ + +/* f = file scale, g = grid 4095.15, o = screen point 255.255, i = screen pixel 255, c = ceil */ +#define _ssfn_i2g(x) ((x) ? (((x) << 16) - (1 << 15)) / ctx->m : 0) +#define _ssfn_g2o(x) (((x) * ctx->m + (1 << 7)) >> 8) +#define _ssfn_g2i(x) (((x) * ctx->m + (1 << 15)) >> 16) +#define _ssfn_g2ic(x) (((x) * ctx->m + (1 << 16) - 1) >> 16) +#define _ssfn_f2i(x) ((((x) << s) * ctx->m + (1 << 15)) >> 16) +#define _ssfn_o2i(x) (((x) + (1 << 7)) >> 8) +#define _ssfn_o2ic(x) ((x + (1 << 8) - 1) >> 8) +#define _ssfn_g2ox(x) ((x) >= (4095 << 4) ? _ssfn_g2o(x) : ctx->h[((x) >> 4)]) +#define _ssfn_g2ix(x) ((x) >= (4095 << 4) ? _ssfn_g2i(x) : _ssfn_o2i(ctx->h[((x) >> 4)])) +#define _ssfn_g2ixc(x) ((x) >= (4095 << 4) ? _ssfn_g2ic(x) : _ssfn_o2ic(ctx->h[((x) >> 4)])) +#define _ssfn_g2oy(y) (_ssfn_g2o(y)) +#define _ssfn_g2iy(y) (_ssfn_g2i(y)) +#define _ssfn_g2iyc(y) (_ssfn_g2ic(y)) +#define _ssfn_igg(y) (((4096 << 4) - (y)) >> (2)) +#define _ssfn_igi(y) ((((4096 << 4) - (y)) * ctx->m + (1 << (15 + 3))) >> (16 + 3)) + + /* parse character table */ + uint8_t *_ssfn_c(const ssfn_font_t *font, uint32_t unicode) + { + uint32_t i, l; + uint8_t *ptr; + + if (!font->characters_offs) return nullptr; + + ptr = (uint8_t *) font + font->characters_offs; + l = (font->quality < 5 && font->characters_offs < 65536) + ? 4 + : (font->characters_offs < 1048576 ? 5 : 6); + + for (i = 0; i < 0x110000; i++) { + if (ptr[0] & 0x80) { + if (ptr[0] & 0x40) { + i += ptr[1] | ((ptr[0] & 0x3f) << 8); + ptr += 2; + } else { + i += ptr[0] & 0x3f; + ptr++; + } + } else { + if (i == unicode) return ptr; + ptr += ptr[0] * l + 10; + } + } + return nullptr; + } + + /* add a line to contour */ + void _ssfn_l(ssfn_t *ctx, int x, int y, int l) + { + if (x > (4096 << 4) - 16) x = (4096 << 4) - 16; + if (y > (4096 << 4) - 16) y = (4096 << 4) - 16; + if (x < -1 || y < -1 || (x == ctx->lx && y == ctx->ly)) return; + + if (ctx->np + 2 >= ctx->mp) { + ctx->mp += 512; + ctx->p = (uint16_t *) realloc(ctx->p, ctx->mp * sizeof(uint16_t)); + if (!ctx->p) throw SSFNException(__FILE__, __LINE__, SSFN_ERR_ALLOC); + } + if (!ctx->np || !l || _ssfn_g2i(ctx->p[ctx->np - 2]) != _ssfn_g2i(x) || + _ssfn_g2i(ctx->p[ctx->np - 1]) != _ssfn_g2i(y)) { + ctx->p[ctx->np++] = x; + ctx->p[ctx->np++] = y; + ctx->lx = x; + ctx->ly = y; + } + if ((ctx->style & 0x200) && x >= 0 && ctx->ix > x) ctx->ix = x; + } + + /* add a Bezier curve to contour */ + void _ssfn_b(ssfn_t *ctx, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3, int l) + { + int m0x, m0y, m1x, m1y, m2x, m2y, m3x, m3y, m4x, m4y, m5x, m5y; + if (l < 8 && (x0 != x3 || y0 != y3)) { + m0x = ((x1 - x0) / 2) + x0; + m0y = ((y1 - y0) / 2) + y0; + m1x = ((x2 - x1) / 2) + x1; + m1y = ((y2 - y1) / 2) + y1; + m2x = ((x3 - x2) / 2) + x2; + m2y = ((y3 - y2) / 2) + y2; + m3x = ((m1x - m0x) / 2) + m0x; + m3y = ((m1y - m0y) / 2) + m0y; + m4x = ((m2x - m1x) / 2) + m1x; + m4y = ((m2y - m1y) / 2) + m1y; + m5x = ((m4x - m3x) / 2) + m3x; + m5y = ((m4y - m3y) / 2) + m3y; + _ssfn_b(ctx, x0, y0, m0x, m0y, m3x, m3y, m5x, m5y, l + 1); + _ssfn_b(ctx, m5x, m5y, m4x, m4y, m2x, m2y, x3, y3, l + 1); + } + _ssfn_l(ctx, x3, y3, l); + } + + /* rasterize contour */ + void _ssfn_r(ssfn_t *ctx) + { + int i, k, l, m, n = 0, x, y, Y, M = 0; + uint16_t *r; + uint8_t *pix = ctx->ret->data; + + for (y = 0; y < ctx->ret->h; y++) { + Y = _ssfn_i2g(y); + r = ctx->r[y]; + for (n = 0, i = 0; i < ctx->np - 3; i += 2) { + if ((ctx->p[i] == 0xffff && ctx->p[i + 1] == 0xffff) || + (ctx->p[i + 2] == 0xffff && ctx->p[i + 3] == 0xffff)) + continue; + if ((ctx->p[i + 1] < Y && ctx->p[i + 3] >= Y) || + (ctx->p[i + 3] < Y && ctx->p[i + 1] >= Y)) { + if (_ssfn_g2iy(ctx->p[i + 1]) == _ssfn_g2iy(ctx->p[i + 3])) + x = (((int) ctx->p[i] + (int) ctx->p[i + 2]) >> 1); + else + x = ((int) ctx->p[i]) + + ((Y - (int) ctx->p[i + 1]) * ((int) ctx->p[i + 2] - (int) ctx->p[i]) / + ((int) ctx->p[i + 3] - (int) ctx->p[i + 1])); + if (y == ctx->u) { + if (x < ctx->uix) { ctx->uix = x; } + if (x > ctx->uax) { ctx->uax = x; } + } + x = _ssfn_g2ox(x - ctx->ix); + for (k = 0; k < n && x > r[k]; k++); + if (n >= ctx->nr[y]) { + ctx->nr[y] = (n < ctx->np) ? ctx->np : (n + 1) << 1; + ctx->r[y] = (uint16_t *) realloc(ctx->r[y], (ctx->nr[y] << 1)); + if (!ctx->r[y]) throw SSFNException(__FILE__, __LINE__, SSFN_ERR_ALLOC); + r = ctx->r[y]; + } + for (l = n; l > k; l--) r[l] = r[l - 1]; + r[k] = x; + n++; + } + } + if (n > 1 && n & 1) { + r[n - 2] = r[n - 1]; + n--; + } + ctx->nr[y] = n; + if (n) { + if (y > M) M = y; + k = y * ctx->ret->pitch; + for (i = 0; i < n - 1; i += 2) { + if (ctx->style & 0x100) { + l = (r[i] + r[i + 1]) >> 9; + if (pix[k + (l >> 3)] & (1 << (l & 7)) ? true : false) { + if (r[i] + 256 > r[i + 1]) + r[i] = r[i + 1] - 128; + else + r[i] += 256; + if (r[i + 1] - 256 > r[i]) r[i + 1] -= 256; + } else { + if (i >= n - 2 || r[i + 1] + 256 < r[i + 2]) r[i + 1] += 256; + } + } + l = ((r[i] + 128) >> 8); + m = ((r[i + 1] + 128) >> 8); + for (; l < m; l++) pix[k + (l >> 3)] ^= 1 << (l & 7); + if (l + 1 > ctx->ret->w) ctx->ret->w = l + 1; + } + } + } + /* fix rounding errors */ + if (M + 1 == ctx->ret->baseline) { + ctx->ret->baseline--; + ctx->u--; + } + } + + /* parse a glyph */ + void _ssfn_g(ssfn_t *ctx, uint8_t *rg, int render) + { + int i, j, nf, m, n, o, ox, oy, ol, t, x, y, a, b, c, d, w, h, s; + uint8_t *raw, *ra, *re, *pix = ctx->ret->data; + + ctx->lx = ctx->ly = ctx->mx = ctx->my = -1; + ol = (ctx->f->quality < 5 && ctx->f->characters_offs < 65536) + ? 4 + : ((ctx->f->characters_offs < 1048576) ? 5 : 6); + s = 16 - ctx->g; + nf = rg[0]; + rg += 10; + + for (h = 0; nf--; rg += ol) { + switch (ol) { + case 4: + o = ((rg[1] << 8) | rg[0]); + ox = rg[2]; + oy = rg[3]; + break; + case 5: + o = (((rg[2] & 0xF) << 16) | (rg[1] << 8) | rg[0]); + ox = (((rg[2] >> 4) & 3) << 8) | rg[3]; + oy = (((rg[2] >> 6) & 3) << 8) | rg[4]; + break; + default: + o = (((rg[3] & 0xF) << 24) | (rg[2] << 16) | (rg[1] << 8) | rg[0]); + ox = ((rg[3] & 0xF) << 8) | rg[4]; + oy = (((rg[3] >> 4) & 0xF) << 8) | rg[5]; + break; + } + ox <<= s; + oy <<= s; + raw = (uint8_t *) ctx->f + o; + if (raw[0] & 0x80) { + t = (raw[0] & 0x60) >> 5; + if (!render && t != SSFN_FRAG_HINTING) break; + switch (t) { + case SSFN_FRAG_LBITMAP: + x = ((((raw[0] >> 2) & 3) << 8) + raw[1]) + 1; + y = (((raw[0] & 3) << 8) | raw[2]) + 1; + raw += 3; + goto bitmap; + + case SSFN_FRAG_BITMAP: + x = (raw[0] & 0x1F) + 1; + y = raw[1] + 1; + raw += 2; + bitmap: + if (ctx->mode == MODE_OUTLINE) { + x <<= 3; + outline: + ctx->lx = ctx->ly = -1; + x <<= s; + y <<= s; + if (ctx->style & 0x200) { + a = (((4096 << 4) - (oy)) >> (3)); + b = (((4096 << 4) - (oy + y)) >> (3)); + if (ctx->ix > ox + a) ctx->ix = ox + a; + } else + a = b = 0; + if (ctx->np) _ssfn_l(ctx, -1, -1, 0); + _ssfn_l(ctx, ox + a, oy, 0); + _ssfn_l(ctx, ox + x + a, oy, 0); + _ssfn_l(ctx, ox + x + b, oy + y, 0); + _ssfn_l(ctx, ox + b, oy + y, 0); + _ssfn_l(ctx, ox + a, oy, 0); + } else { + a = x << 3; + b = y << s; + c = _ssfn_g2i(oy); + n = _ssfn_g2i(ox); + w = _ssfn_g2i(x << (3 + s)); + h = _ssfn_g2i(b); + if (c + h >= ctx->ret->h) c = ctx->ret->h - h; /* due to rounding */ + c = t = c * ctx->ret->pitch; + for (j = 0; j < h; j++) { + o = (((j << 8) * (y << 8) / (h << 8)) >> 8) * x; + for (i = 0; i < w; i++) { + m = ((i << 8) * (a << 8) / (w << 8)) >> 8; + if (raw[o + (m >> 3)] & (1 << (m & 7))) { + d = n + ((ctx->style & 0x200) ? _ssfn_igi(oy + (j << s) + 127) : 0) + i; + pix[c + (d >> 3)] |= 1 << (d & 7); + d++; + if ((ctx->style & 0x100) && (d >> 3) < ctx->ret->pitch) { + pix[c + (d >> 3)] |= 1 << (d & 7); + d++; + if (ctx->size > 127 && (d >> 3) < ctx->ret->pitch) { + pix[c + (d >> 3)] |= 1 << (d & 7); + } + } + if (d > ctx->ret->w) ctx->ret->w = d; + d = ox + _ssfn_i2g(d); + if ((ctx->style & 0x200) && ctx->ix > d) ctx->ix = d; + if (_ssfn_g2i(oy) + j == ctx->u) { + if (d < ctx->uix) { ctx->uix = d; } + if (d > ctx->uax) { ctx->uax = d; } + } + } + } + c += ctx->ret->pitch; + } + } + h = 0; + break; + + case SSFN_FRAG_PIXMAP: + x = (((raw[0] & 12) << 6) | raw[1]) + 1; + y = (((raw[0] & 3) << 8) | raw[2]) + 1; + n = ((raw[4] << 8) | raw[3]) + 1; + raw += 5; + if (ctx->mode == MODE_OUTLINE) goto outline; + if (raw[-5] & 0x10) { /* todo: direct ARGB values in pixmap fragment */ + } + a = x * y; + if (a >= (ctx->nr[0] << 1)) { + ctx->nr[0] = (a + 1) >> 1; + ctx->r[0] = (uint16_t *) realloc(ctx->r[0], (ctx->nr[0] << 1)); + if (!ctx->r[0]) throw SSFNException(__FILE__, __LINE__, SSFN_ERR_ALLOC); + } + ctx->ret->cmap = (uint32_t *) ((uint8_t *) ctx->f + ctx->f->size - 964); + for (re = raw + n, ra = (uint8_t *) ctx->r[0], i = 0; i < a && raw < re;) { + c = (raw[0] & 0x7F) + 1; + if (raw[0] & 0x80) { + for (j = 0; j < c; j++) ra[i++] = raw[1]; + raw += 2; + } else { + raw++; + for (j = 0; j < c; j++) ra[i++] = *raw++; + } + } + b = y << s; + c = _ssfn_g2i(oy); + n = _ssfn_g2i(ox); + w = _ssfn_g2i(x << s); + h = _ssfn_g2i(b); + if (c + h >= ctx->ret->h) c = ctx->ret->h - h; /* due to rounding */ + c = t = c * ctx->ret->pitch; + for (j = 0; j < h; j++) { + o = (((j << 8) * (y << 8) / (h << 8)) >> 8) * x; + for (i = 0; i < w; i++) { + m = ((i << 8) * (x << 8) / (w << 8)) >> 8; + if (ra[o + m] < 0xF0) { + d = n + ((ctx->style & 0x200) ? _ssfn_igi(oy + (j << s) + 127) : 0) + i; + re = (uint8_t *) &ctx->ret->cmap[ra[o + m]]; + a = (re[0] + re[1] + re[2] + 255) >> 2; + if (a > 127) pix[c + (d >> 3)] |= 1 << (d & 7); + if (d > ctx->ret->w) ctx->ret->w = d; + } + } + c += ctx->ret->pitch; + } + h = 0; + break; + + case SSFN_FRAG_HINTING: + if (raw[0] & 0x10) { + n = ((raw[0] & 0xF) << 8) | raw[1]; + raw += 2; + } else { + n = raw[0] & 0xF; + raw++; + } + if (render || !ox) { + raw += n << (ctx->f->features & SSFN_FEAT_HBIGCRD ? 1 : 0); + continue; + } + y = 4096; + x = ((ox >> s) - 1) << (s - 4); + ctx->h[y++] = x; + for (n++; n-- && x < 4096;) { + x = raw[0]; + raw++; + if (ctx->f->features & SSFN_FEAT_HBIGCRD) { + x |= (raw[0] << 8); + raw++; + } + x <<= (s - 4); + ctx->h[y++] = x; + } + if (y < 4096) ctx->h[y++] = 65535; + h = 1; + break; + } + } else { + if (!render && h) break; + if (raw[0] & 0x40) { + n = ((raw[0] & 0x3F) << 8) | raw[1]; + raw += 2; + } else { + n = raw[0] & 0x3F; + raw++; + } + if (ctx->f->quality < 5) { + x = raw[0]; + y = raw[1]; + raw += 2; + } else { + x = ((raw[0] & 3) << 8) | raw[1]; + y = ((raw[0] & 0x30) << 4) | raw[2]; + raw += 3; + } + x <<= s; + y <<= s; + y += oy; + x += ox + (ctx->style & 0x200 ? _ssfn_igg(y) : 0); + if (render) { + if (ctx->np) { + _ssfn_l(ctx, ctx->mx, ctx->my, 0); + _ssfn_l(ctx, -1, -1, 0); + } + _ssfn_l(ctx, x, y, 0); + } + ctx->lx = ctx->mx = x; + ctx->ly = ctx->my = y; + for (n++; n--;) { + t = ctx->g < 8 ? (raw[0] >> 7) | ((raw[1] >> 6) & 2) : raw[0] & 3; + x = y = a = b = c = d = j = 0; + switch (ctx->g) { + case 4: + case 5: + case 6: + case 7: + x = raw[0] & 0x7F; + y = raw[1] & 0x7F; + switch (t) { + case 0: + raw += raw[0] & 4 ? 5 : 2; + break; + case 1: + raw += 2; + break; + case 2: + a = raw[2] & 0x7F; + b = raw[3] & 0x7F; + raw += 4; + break; + case 3: + a = raw[2] & 0x7F; + b = raw[3] & 0x7F; + c = raw[4] & 0x7F; + d = raw[5] & 0x7F; + raw += 6; + break; + } + break; + + case 8: + x = raw[1]; + y = raw[2]; + switch (t) { + case 0: + raw += raw[0] & 4 ? 5 : 2; + break; + case 1: + raw += 3; + break; + case 2: + a = raw[3]; + b = raw[4]; + raw += 5; + break; + case 3: + a = raw[3]; + b = raw[4]; + c = raw[5]; + d = raw[6]; + raw += 7; + break; + } + break; + + case 9: + x = ((raw[0] & 4) << 6) | raw[1]; + y = ((raw[0] & 8) << 5) | raw[2]; + switch (t) { + case 0: + raw += raw[0] & 4 ? 5 : 2; + break; + case 1: + raw += 3; + break; + case 2: + a = ((raw[0] & 16) << 4) | raw[3]; + b = ((raw[0] & 32) << 3) | raw[4]; + raw += 5; + break; + case 3: + a = ((raw[0] & 16) << 4) | raw[3]; + b = ((raw[0] & 32) << 3) | raw[4]; + c = ((raw[0] & 64) << 2) | raw[5]; + d = ((raw[0] & 128) << 1) | raw[6]; + raw += 7; + break; + } + break; + + default: + x = ((raw[0] & 12) << 6) | raw[1]; + y = ((raw[0] & 48) << 4) | raw[2]; + switch (t) { + case 0: + raw += raw[0] & 4 ? 5 : 2; + break; + case 1: + raw += 3; + break; + case 2: + a = ((raw[3] & 3) << 8) | raw[4]; + b = ((raw[3] & 12) << 6) | raw[5]; + raw += 6; + break; + case 3: + a = ((raw[3] & 3) << 8) | raw[4]; + b = ((raw[3] & 12) << 6) | raw[5]; + c = ((raw[3] & 48) << 4) | raw[6]; + d = ((raw[3] & 192) << 2) | raw[7]; + raw += 8; + break; + } + break; + } + x <<= s; + y <<= s; + a <<= s; + b <<= s; + c <<= s; + d <<= s; + x += ox; + y += oy; + a += ox; + b += oy; + c += ox; + d += oy; + if (ctx->style & 0x200) { + x += _ssfn_igg(y); + a += _ssfn_igg(b); + c += _ssfn_igg(d); + } + if (render) { + switch (t) { + case 0: /* this v1.0 renderer does not support colored contours */ + break; + case 1: + _ssfn_l(ctx, x, y, 0); + break; + case 2: + _ssfn_b(ctx, ctx->lx, ctx->ly, ((a - ctx->lx) >> 1) + ctx->lx, + ((b - ctx->ly) >> 1) + ctx->ly, ((x - a) >> 1) + a, ((y - b) >> 1) + b, x, + y, 0); + break; + case 3: + _ssfn_b(ctx, ctx->lx, ctx->ly, a, b, c, d, x, y, 0); + break; + } + } else if (t == 1 && x >= 0 && y >= 0) { + a = ((ctx->lx < x) ? x - ctx->lx : ctx->lx - x) >> 4; + b = ((ctx->ly < y) ? y - ctx->ly : ctx->ly - y) >> 4; + c = (ctx->lx + x) >> 5; + if (a < 2) + ctx->h[4096 + (!ctx->h[4096 + c] && c && ctx->h[4096 + c - 1] ? c - 1 : c)] += b; + } + ctx->lx = x; + ctx->ly = y; + } + } + } + + if (!render && !h) { + for (j = m = x = y = 0; j < 4096; j++) { + if (ctx->h[4096 + j] >= 4096 / SSFN_HINTING_THRESHOLD) { + if (!j) + m++; + else { + ctx->h[4096 + m++] = j - x; + x = j; + } + } + } + if (m < 4096) ctx->h[4096 + m] = 65535; + } + } + + /** + * Load a font or font collection into renderer context + * + * @param ctx rendering context + * @param font SSFN font or font collection in memory + */ + void _ssfn_load(ssfn_t *ctx, const ssfn_font_t *font) + { + ssfn_font_t *ptr, *end; + + if (!ctx || !font) throw SSFNException(__FILE__, __LINE__, SSFN_ERR_INVINP); + + if (!memcmp(font->magic, SSFN_COLLECTION, 4)) { + end = (ssfn_font_t *) ((uint8_t *) font + font->size); + for (ptr = (ssfn_font_t *) ((uint8_t *) font + 8); ptr < end; + ptr = (ssfn_font_t *) ((uint8_t *) ptr + ptr->size)) + _ssfn_load(ctx, ptr); + } else { + if (memcmp(font->magic, SSFN_MAGIC, 4) || + memcmp((uint8_t *) font + font->size - 4, SSFN_ENDMAGIC, 4) || + font->family > FAMILY_HAND || font->fragments_offs > font->size || + font->characters_offs > font->size || font->kerning_offs > font->size || + font->fragments_offs >= font->characters_offs || font->quality > 8) { + throw SSFNException(__FILE__, __LINE__, SSFN_ERR_BADFILE); + } else { + ctx->len[font->family]++; + ctx->fnt[font->family] = (const ssfn_font_t **) realloc( + ctx->fnt[font->family], ctx->len[font->family] * sizeof(void *)); + if (!ctx->fnt[font->family]) + throw SSFNException(__FILE__, __LINE__, SSFN_ERR_ALLOC); + else + ctx->fnt[font->family][ctx->len[font->family] - 1] = font; + } + } + } + + /** + * Set up rendering parameters + * + * @param ctx rendering context + * @param family one of SSFN_FAMILY_* + * @param name NULL or UTF-8 string if family is SSFN_FAMILY_BYNAME + * @param style OR'd values of SSFN_STYLE_* + * @param size how big glyph it should render, 8 - 255 + */ + void _ssfn_select(ssfn_t *ctx, int family, int style, int size) + { + if (!ctx) throw SSFNException(__FILE__, __LINE__, SSFN_ERR_INVINP); + if ((style & ~0xCF)) throw SSFNException(__FILE__, __LINE__, SSFN_ERR_BADSTYLE); + if (size < 8 || size > 255) throw SSFNException(__FILE__, __LINE__, SSFN_ERR_BADSIZE); + + ctx->np = ctx->mp = 0; + if (ctx->p) { + free(ctx->p); + ctx->p = nullptr; + } + ctx->f = nullptr; + ctx->family = family; + ctx->style = style; + ctx->size = size; + ctx->mode = MODE_BITMAP; + } + + /** + * Glyph renderer + * + * @param ctx rendering context + * @param unicode character to render + * @return newly allocated rasterized glyph + */ + ssfn_glyph_t *_ssfn_render(ssfn_t *ctx, uint32_t unicode) + { + ssfn_font_t **fl; + int i, j, s, h, p, m, n, bt, bl; + int l, x, y; + uint8_t *rg = nullptr, c, d; + + if (!ctx) return nullptr; + if (ctx->size < 8) throw SSFNException(__FILE__, __LINE__, SSFN_ERR_NOFACE); + + if (ctx->s) { + ctx->f = (ssfn_font_t *) ctx->s; + rg = _ssfn_c(ctx->f, unicode); + if (!rg) rg = _ssfn_c(ctx->f, unicode); + } else { + p = ctx->family; + again: + if (p == SSFN_FAMILY_ANY) { + n = 0; + m = 4; + } else + n = m = p; + for (; n <= m; n++) { + fl = (ssfn_font_t **) ctx->fnt[n]; + if (ctx->style & 3) { + /* check if we have a specific ctx->f for the requested style */ + for (i = 0; i < ctx->len[n]; i++) + if ((fl[i]->style & 3) == (ctx->style & 3) && (rg = _ssfn_c(fl[i], unicode))) { + ctx->f = fl[i]; + break; + } + /* if bold italic was requested, check if we have at least bold or italic */ + if (!rg && (ctx->style & 3) == 3) + for (i = 0; i < ctx->len[n]; i++) + if ((fl[i]->style & 3) && (rg = _ssfn_c(fl[i], unicode))) { + ctx->f = fl[i]; + break; + } + } + /* last resort, get the first ctx->f which has a glyph for this unicode, no matter style */ + if (!rg) { + for (i = 0; i < ctx->len[n]; i++) + if ((rg = _ssfn_c(fl[i], unicode))) { + ctx->f = fl[i]; + break; + } + } + } + /* if glyph still not found, try any family group */ + if (!rg) { + if (p != SSFN_FAMILY_ANY) { + p = SSFN_FAMILY_ANY; + goto again; + } + } + } + if (!rg) throw SSFNException(__FILE__, __LINE__, SSFN_ERR_NOGLYPH); + + ctx->style &= 0xFF; + if ((ctx->style & 1) && !(ctx->f->style & 1)) ctx->style |= 0x100; + if ((ctx->style & 2) && !(ctx->f->style & 2)) ctx->style |= 0x200; + if (ctx->f->family == FAMILY_MONOSPACE) ctx->style |= SSFN_STYLE_ABS_SIZE; + + ctx->g = 4 + ctx->f->quality; + ctx->np = 0; + + s = 16 - ctx->g; + if (ctx->mode == MODE_OUTLINE) { + h = ctx->size; + p = 0; + } else { + if (!(((rg[2] & 0x0F) << 8) | rg[6]) || ctx->style & SSFN_STYLE_ABS_SIZE) + h = ctx->size; + else + h = (4096 << 4) * ctx->size / ((ctx->f->baseline - ctx->f->bbox_top) << s); + p = (h + (ctx->style & 0x100 ? 2 : 0) + (ctx->style & 0x200 ? h >> 2 : 0)); + if (p > 255) { + p = h = 255; + if (ctx->style & 0x100) h -= 2; + if (ctx->style & 0x200) h = h * 4 / 5; + } + if (ctx->mode == MODE_BITMAP) p = (p + 7) >> 3; + } + ctx->m = h; + + if (!ctx->h) ctx->h = (uint16_t *) malloc(4096 * 2 * sizeof(uint16_t)); + if (!ctx->h) throw SSFNException(__FILE__, __LINE__, SSFN_ERR_ALLOC); + + if (!(ctx->style & SSFN_STYLE_NOHINTING)) { + memset(&ctx->h[4096], 0, 4096 * sizeof(uint16_t)); + _ssfn_g(ctx, rg, 0); + } else + ctx->h[4096] = 65535; + + ctx->h[0] = 0; + for (i = j = x = y = m = 0; i < 4096 && j < 4095; i++) { + y = ctx->h[4096 + i] == 65535 ? 4095 - j : ctx->h[4096 + i]; + j += y; + if (j == x) { + ctx->h[j] = (((j << 4) * h + (1 << 15)) >> 16) << 8; + if (!y) j++; + } else { + y = _ssfn_g2o(y << 4); + m += y; + if ((i & 1) || y < 256) { continue; } + m &= ~0xFF; + n = ctx->h[x]; + for (l = 0; l + x <= j; l++) ctx->h[x + l] = n + ((m - n) * l / (j - x)); + } + x = j; + } + ctx->uix = _ssfn_g2ixc((((rg[2] & 0x0F) << 8) | rg[6]) << s); + ctx->uax = _ssfn_g2iyc((((rg[2] & 0xF0) << 4) | rg[7]) << s); + if (ctx->mode == MODE_NONE) return nullptr; + + bl = ((((rg[3] & 0x0F) << 8) | rg[8])) << s; + bt = ((((rg[3] & 0xF0) << 4) | rg[9])) << s; + i = p * h; + ctx->ret = (ssfn_glyph_t *) malloc(i + 8 + sizeof(uint8_t *)); + if (!ctx->ret) return nullptr; + + memset(&ctx->ret->data, 0, i); + ctx->ret->cmap = nullptr; + ctx->ret->mode = ctx->mode; + ctx->ret->pitch = p; + ctx->ret->w = 0; + ctx->ret->h = h; + ctx->ret->baseline = (((ctx->f->baseline << s) - bt) * h + (1 << 16) - 1) >> 16; + ctx->u = ctx->ret->baseline + ((((ctx->f->underline - ctx->f->baseline) << s) * h) >> 16); + + ctx->ret->adv_x = ctx->uix; + ctx->ret->adv_y = ctx->uax; + + ctx->ix = ctx->uix = 4096 << 4; + ctx->uax = 0; + _ssfn_g(ctx, rg, 1); + + if (!(ctx->style & 0x200) || ctx->ix == 4096 << 4) ctx->ix = 0; + if (ctx->mode == MODE_OUTLINE) { + if (ctx->np > p * h) { + ctx->ret = (ssfn_glyph_t *) realloc(ctx->ret, ctx->np + 8 + sizeof(uint8_t *)); + if (!ctx->ret) throw SSFNException(__FILE__, __LINE__, SSFN_ERR_ALLOC); + } + for (s = i = 0; i < ctx->np; i += 2) { + c = ctx->p[i + 0] == 0xffff ? 0xff : _ssfn_g2ix(ctx->p[i + 0] + bl); + d = ctx->p[i + 1] == 0xffff ? 0xff : _ssfn_g2iy(ctx->p[i + 1] + bt); + if (s < 2 || ctx->ret->data[s - 2] != c || ctx->ret->data[s - 1] != d) { + ctx->ret->data[s++] = c; + ctx->ret->data[s++] = d; + } + } + ctx->ret->pitch = s; + ctx->ret = (ssfn_glyph_t *) realloc(ctx->ret, s + 8 + sizeof(uint8_t *)); + if (!ctx->ret) throw SSFNException(__FILE__, __LINE__, SSFN_ERR_ALLOC); + } else { + _ssfn_r(ctx); + if (ctx->style & SSFN_STYLE_STHROUGH) { + if (ctx->ret->w < ctx->ret->adv_x) ctx->ret->w = ctx->ret->adv_x; + memset(&ctx->ret->data[(ctx->ret->baseline - (ctx->size >> 2)) * p], 0xFF, + (ctx->size / 64 + 2) * p); + } + if (ctx->style & SSFN_STYLE_UNDERLINE) { + if (ctx->ret->w < ctx->ret->adv_x) ctx->ret->w = ctx->ret->adv_x; + if (ctx->uax > ctx->ix) ctx->uax = _ssfn_g2i(ctx->uax - ctx->ix); + if (ctx->uix != 4096 << 4) + ctx->uix = _ssfn_g2i(ctx->uix - ctx->ix); + else + ctx->uix = ctx->ret->w + 3; + m = ctx->u * p; + n = ctx->size > 127 ? 2 : 1; + while (n--) { + if (ctx->uix > 3) { + j = ctx->uix - 3; + if (ctx->mode == MODE_BITMAP) + for (i = 0; i < j; i++) ctx->ret->data[m + (i >> 3)] |= 1 << (i & 7); + else + memset(&ctx->ret->data[m], 0xFF, j); + } + if (ctx->uax) { + j = ctx->uax + 2; + if (ctx->mode == MODE_BITMAP) + for (i = j; i < ctx->ret->w; i++) ctx->ret->data[m + (i >> 3)] |= 1 << (i & 7); + else + memset(&ctx->ret->data[m + j], 0xFF, p - j); + } + m += p; + } + } + if (ctx->ret->adv_y) ctx->ret->baseline = ctx->ret->w >> 1; + } + return ctx->ret; + } + + /** + * Free renderer context + * + * @param ctx rendering context + */ + void _ssfn_free(ssfn_t *ctx) + { + int i; + + if (!ctx) return; + + for (i = 0; i < 5; i++) + if (ctx->fnt[i]) free(ctx->fnt[i]); + if (ctx->p) free(ctx->p); + for (i = 0; i < 256; i++) + if (ctx->r[i]) free(ctx->r[i]); + if (ctx->h) free(ctx->h); + memset(ctx, 0, sizeof(ssfn_t)); + } + // helper function to truncate a string to a segment starting with "src/"; + + std::string truncpath(const std::string &path) + { + std::size_t found = path.find("src/"); + if (found != std::string::npos) + return path.substr(found); + else + return path; + } +} // namespace + +/* public API implementation */ + +// constructor: create and initialize context and load font +ScalableFont::ScalableFont() +{ + auto *ctxptr = new ssfn_t; + memset(ctxptr, 0, sizeof(ssfn_t)); + _ssfn_load(ctxptr, ssfn_sans_font); + ctx = (void *) ctxptr; +} + +// clean up +ScalableFont::~ScalableFont() +{ + auto *ctxptr = (ssfn_t *) ctx; + _ssfn_free(ctxptr); + delete ctxptr; +} + +// set font settings +void ScalableFont::select_font(int family, int style, int size) +{ + _ssfn_select((ssfn_t *) ctx, family, style, size); +} + +unsigned char *ScalableFont::create_pixmap(const std::string &text, int &width, int &height, + const unsigned char *font, const unsigned char *frame, + const unsigned char *back) +{ + auto *ctxptr = (ssfn_t *) ctx; + ssfn_glyph_t *g; + + // get a font size specific spacing for a border + g = _ssfn_render(ctxptr, ' '); + int xspace = g->adv_x; + free(g); + + // dry run to determine size of pixmap + width = 0; + int miny = 1073741824; + int maxy = 0; + for (auto c : text + "gll") { // append these characters for consistent spacing + if (c == '_') c = ' '; // ugly hack to work around font issue + + // render character and apply its width + g = _ssfn_render(ctxptr, c); + width += g->adv_x; + + // loop over bitmap to find minimum and maximum y position + for (int y = 0; y < g->h; ++y) { + const int ypos = g->h - 1 - y + g->baseline; + for (int x = 0, i = 0, m = 1; x < g->w; ++x, m <<= 1) { + if (m > 0x80) { + m = 1; + ++i; + } + if (g->data[y * g->pitch + i] & m) { + miny = std::min(miny, ypos); + maxy = std::max(maxy, ypos); + } + } + } + free(g); + } + + int xhalf = xspace / 2; + height = maxy - miny + 1 + 3 * xspace; + + // allocate and fill pixmap with background and frame color + + auto *pixmap = new unsigned char[width * height * 3]; + for (int y = 0; y < height; ++y) { + int yoffs = 3 * y * width; + for (int x = 0; x < width; ++x) { + if ((y < xhalf) || (y >= height - xhalf) || (x < xhalf) || (x >= width - xhalf)) { + pixmap[yoffs + 3 * x] = frame[0]; + pixmap[yoffs + 3 * x + 1] = frame[1]; + pixmap[yoffs + 3 * x + 2] = frame[2]; + } else { + pixmap[yoffs + 3 * x] = back[0]; + pixmap[yoffs + 3 * x + 1] = back[1]; + pixmap[yoffs + 3 * x + 2] = back[2]; + } + } + } + + // now render each character again and change the pixels in the pixmap accordingly + int penx = 2 * xspace; + for (auto c : text) { + if (c == '_') c = ' '; // ugly hack to work around font issue + + g = _ssfn_render(ctxptr, c); + for (int y = 0; y < g->h; ++y) { + const int yoffs = (g->h - 1 - y + g->baseline - miny + xspace + xhalf / 2) * width * 3; + for (int x = 0, i = 0, m = 1; x < g->w; ++x, m <<= 1) { + if (m > 0x80) { + m = 1; + ++i; + } + const int xoffs = (penx + x) * 3; + if (g->data[y * g->pitch + i] & m) { + pixmap[yoffs + xoffs] = font[0]; + pixmap[yoffs + xoffs + 1] = font[1]; + pixmap[yoffs + xoffs + 2] = font[2]; + } + } + } + penx += g->adv_x; + free(g); + } + return pixmap; +} + +SSFNException::SSFNException(const std::string &file, int line, int flag) +{ + message = fmt::format("In file {}:{} ", truncpath(file), line); + if ((flag < SSFN_OK) || (flag > SSFN_ERR_NOGLYPH)) { + message.append("Unknown Error"); + } else { + message.append(ssfn_errstr[flag]); + } +} +} // namespace SSFN diff --git a/src/GRAPHICS/scalable_font.h b/src/GRAPHICS/scalable_font.h new file mode 100644 index 00000000000..ef09e0d8965 --- /dev/null +++ b/src/GRAPHICS/scalable_font.h @@ -0,0 +1,70 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef LMP_SCALABLE_FONT_H +#define LMP_SCALABLE_FONT_H + +#include +#include + +namespace SSFN { + +/* rendering modes */ +enum { MODE_NONE, MODE_OUTLINE, MODE_BITMAP }; + +/* font family group */ +enum { FAMILY_SERIF, FAMILY_SANS, FAMILY_DECOR, FAMILY_MONOSPACE, FAMILY_HAND }; + +/* font style flags */ +enum { STYLE_REGULAR, STYLE_BOLD, STYLE_ITALIC }; + +/** text to pixmap renderer class */ +class ScalableFont { + public: + ScalableFont(); + ~ScalableFont(); + + void select_font(int family, int style, int size); + unsigned char *create_pixmap(const std::string &text, int &width, int &height, + const unsigned char *font, const unsigned char *frame, + const unsigned char *back); + + private: + void *ctx; +}; + +/** Font renderer exception class */ +class SSFNException : public std::exception { + public: + SSFNException() = delete; + /** Thrown during font processing + * + * \param file source file where exception was thrown + * \param line line in source file where exception was thrown + * \param flag select error message */ + explicit SSFNException(const std::string &file, int line, int flag); + + /** Retrieve message describing the thrown exception + * + * This function provides the message that can be retrieved when the corresponding + * exception is caught. + * + * \return String with error message */ + [[nodiscard]] const char *what() const noexcept override { return message.c_str(); } + + private: + std::string message; +}; +} // namespace SSFN + +#endif diff --git a/src/GRAPHICS/scalable_sans_font.h b/src/GRAPHICS/scalable_sans_font.h new file mode 100644 index 00000000000..b744a5e2116 --- /dev/null +++ b/src/GRAPHICS/scalable_sans_font.h @@ -0,0 +1,1761 @@ +/* -*- c++ -*- ------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef LMP_SCALABLE_SANS_FONT_H +#define LMP_SCALABLE_SANS_FONT_H + +// clang-format off + +namespace { +// font file VeraR.sfn as const byte array +const unsigned char VeraR_sfn[] = { + 0x53, 0x53, 0x46, 0x4e, 0x6f, 0x51, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbb, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x1a, 0x00, + 0xff, 0x00, 0xe4, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x08, 0x37, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x21, 0x49, 0x00, 0x00, 0x42, 0x69, 0x74, 0x73, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x20, 0x56, 0x65, 0x72, 0x61, 0x20, 0x53, 0x61, 0x6e, 0x73, 0x00, + 0x42, 0x69, 0x74, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x20, 0x56, 0x65, + 0x72, 0x61, 0x20, 0x53, 0x61, 0x6e, 0x73, 0x00, 0x52, 0x6f, 0x6d, 0x61, + 0x6e, 0x00, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x31, 0x2e, + 0x31, 0x30, 0x00, 0x42, 0x69, 0x74, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x00, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, + 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x33, + 0x20, 0x62, 0x79, 0x20, 0x42, 0x69, 0x74, 0x73, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x41, 0x6c, 0x6c, 0x20, + 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x52, 0x65, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x64, 0x2e, 0x00, 0x03, 0x00, 0x99, 0x01, 0x00, 0x00, 0x01, + 0x57, 0x00, 0x01, 0x57, 0x99, 0x01, 0x00, 0x99, 0x03, 0x00, 0x86, 0x01, + 0x43, 0x86, 0x01, 0x43, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x86, 0x03, + 0x00, 0x00, 0x01, 0x11, 0x00, 0x01, 0x11, 0x15, 0x01, 0x00, 0x15, 0x01, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x11, 0x00, 0x01, 0x11, 0x37, 0x01, + 0x0f, 0x56, 0x01, 0x02, 0x56, 0x01, 0x00, 0x37, 0x01, 0x00, 0x00, 0x03, + 0x0e, 0x00, 0x01, 0x0e, 0x2e, 0x01, 0x00, 0x2e, 0x01, 0x00, 0x00, 0x01, + 0x0e, 0x00, 0x03, 0x20, 0x00, 0x01, 0x07, 0x00, 0x01, 0x00, 0x1c, 0x01, + 0x19, 0x1c, 0x01, 0x20, 0x00, 0x1b, 0x3f, 0x00, 0x01, 0x36, 0x23, 0x01, + 0x4f, 0x23, 0x01, 0x58, 0x00, 0x01, 0x65, 0x00, 0x01, 0x5d, 0x23, 0x01, + 0x77, 0x23, 0x01, 0x77, 0x30, 0x01, 0x59, 0x30, 0x01, 0x53, 0x4c, 0x01, + 0x6d, 0x4c, 0x01, 0x6d, 0x59, 0x01, 0x4f, 0x59, 0x01, 0x46, 0x7c, 0x01, + 0x39, 0x7c, 0x01, 0x42, 0x59, 0x01, 0x28, 0x59, 0x01, 0x20, 0x7c, 0x01, + 0x12, 0x7c, 0x01, 0x1b, 0x59, 0x01, 0x00, 0x59, 0x01, 0x00, 0x4c, 0x01, + 0x1e, 0x4c, 0x01, 0x25, 0x30, 0x01, 0x0a, 0x30, 0x01, 0x0a, 0x23, 0x01, + 0x28, 0x23, 0x01, 0x31, 0x00, 0x01, 0x3f, 0x00, 0x19, 0x2c, 0x9d, 0x01, + 0x24, 0x9d, 0x01, 0x24, 0x83, 0x02, 0x12, 0x81, 0x1b, 0x83, 0x02, 0x00, + 0x7b, 0x09, 0x7f, 0x01, 0x00, 0x6c, 0x02, 0x12, 0x74, 0x09, 0x72, 0x02, + 0x24, 0x77, 0x1b, 0x77, 0x01, 0x24, 0x51, 0x02, 0x09, 0x46, 0x11, 0x4d, + 0x02, 0x00, 0x32, 0x00, 0x3f, 0x02, 0x0a, 0x1c, 0x00, 0x24, 0x02, 0x24, + 0x14, 0x12, 0x15, 0x01, 0x24, 0x00, 0x01, 0x2c, 0x00, 0x01, 0x2c, 0x14, + 0x02, 0x3c, 0x15, 0x34, 0x14, 0x02, 0x4a, 0x18, 0x43, 0x16, 0x01, 0x4a, + 0x27, 0x02, 0x3c, 0x22, 0x43, 0x24, 0x02, 0x2c, 0x20, 0x34, 0x20, 0x01, + 0x2c, 0x44, 0x02, 0x49, 0x4e, 0x40, 0x46, 0x02, 0x52, 0x63, 0x52, 0x56, + 0x02, 0x48, 0x7a, 0x52, 0x71, 0x02, 0x2c, 0x83, 0x3f, 0x82, 0x01, 0x2c, + 0x9d, 0x04, 0x14, 0x22, 0x01, 0x14, 0x00, 0x02, 0x05, 0x05, 0x0a, 0x00, + 0x02, 0x00, 0x11, 0x00, 0x0a, 0x02, 0x04, 0x1c, 0x00, 0x18, 0x02, 0x14, + 0x22, 0x09, 0x20, 0x04, 0x00, 0x00, 0x01, 0x00, 0x25, 0x02, 0x10, 0x1f, + 0x0b, 0x24, 0x02, 0x16, 0x12, 0x16, 0x1a, 0x02, 0x11, 0x07, 0x16, 0x0b, + 0x02, 0x00, 0x00, 0x0c, 0x02, 0x07, 0x10, 0x00, 0x02, 0x04, 0x06, 0x09, + 0x00, 0x02, 0x00, 0x18, 0x00, 0x0d, 0x02, 0x04, 0x29, 0x00, 0x22, 0x02, + 0x10, 0x2f, 0x09, 0x2f, 0x02, 0x1c, 0x29, 0x17, 0x2f, 0x02, 0x20, 0x18, + 0x20, 0x22, 0x02, 0x1c, 0x06, 0x20, 0x0d, 0x02, 0x10, 0x00, 0x17, 0x00, + 0x07, 0x1d, 0x00, 0x02, 0x32, 0x09, 0x2b, 0x00, 0x02, 0x3a, 0x23, 0x3a, + 0x13, 0x02, 0x32, 0x3c, 0x3a, 0x33, 0x02, 0x1d, 0x45, 0x2b, 0x45, 0x02, + 0x07, 0x3c, 0x0f, 0x45, 0x02, 0x00, 0x23, 0x00, 0x33, 0x02, 0x07, 0x09, + 0x00, 0x12, 0x02, 0x1d, 0x00, 0x0f, 0x00, 0x07, 0x10, 0x00, 0x02, 0x04, + 0x07, 0x08, 0x00, 0x02, 0x00, 0x18, 0x00, 0x0d, 0x02, 0x04, 0x2a, 0x00, + 0x23, 0x02, 0x10, 0x30, 0x08, 0x30, 0x02, 0x1b, 0x2a, 0x17, 0x30, 0x02, + 0x1f, 0x18, 0x1f, 0x23, 0x02, 0x1b, 0x07, 0x1f, 0x0d, 0x02, 0x10, 0x00, + 0x17, 0x00, 0x03, 0x4f, 0x00, 0x01, 0x5d, 0x00, 0x01, 0x0d, 0x83, 0x01, + 0x00, 0x83, 0x01, 0x4f, 0x00, 0x07, 0x1e, 0x00, 0x02, 0x33, 0x09, 0x2b, + 0x00, 0x02, 0x3b, 0x22, 0x3b, 0x12, 0x02, 0x33, 0x3b, 0x3b, 0x32, 0x02, + 0x1e, 0x45, 0x2b, 0x45, 0x02, 0x09, 0x3b, 0x10, 0x45, 0x02, 0x00, 0x22, + 0x00, 0x32, 0x02, 0x09, 0x09, 0x00, 0x13, 0x02, 0x1e, 0x00, 0x10, 0x00, + 0x06, 0x0f, 0x00, 0x02, 0x03, 0x0d, 0x07, 0x06, 0x02, 0x00, 0x1b, 0x00, + 0x14, 0x02, 0x09, 0x30, 0x00, 0x28, 0x02, 0x20, 0x39, 0x12, 0x39, 0x02, + 0x2f, 0x36, 0x28, 0x39, 0x02, 0x3c, 0x2e, 0x36, 0x33, 0x01, 0x0f, 0x00, + 0x1b, 0x2b, 0x33, 0x01, 0x56, 0x5f, 0x02, 0x5e, 0x4f, 0x5b, 0x58, 0x02, + 0x61, 0x3d, 0x61, 0x46, 0x01, 0x71, 0x3d, 0x02, 0x6c, 0x53, 0x70, 0x48, + 0x02, 0x5f, 0x69, 0x67, 0x5e, 0x01, 0x77, 0x80, 0x01, 0x61, 0x80, 0x01, + 0x55, 0x74, 0x02, 0x43, 0x80, 0x4c, 0x7c, 0x02, 0x2e, 0x83, 0x39, 0x83, + 0x02, 0x0d, 0x78, 0x19, 0x83, 0x02, 0x00, 0x5a, 0x00, 0x6c, 0x02, 0x05, + 0x45, 0x00, 0x4f, 0x02, 0x17, 0x33, 0x0b, 0x3b, 0x02, 0x10, 0x28, 0x13, + 0x2e, 0x02, 0x0e, 0x1e, 0x0e, 0x23, 0x02, 0x17, 0x08, 0x0e, 0x11, 0x02, + 0x31, 0x00, 0x21, 0x00, 0x02, 0x3f, 0x01, 0x37, 0x00, 0x02, 0x4d, 0x06, + 0x46, 0x03, 0x01, 0x4d, 0x15, 0x02, 0x3f, 0x0f, 0x46, 0x11, 0x02, 0x33, + 0x0d, 0x38, 0x0d, 0x02, 0x24, 0x12, 0x29, 0x0d, 0x02, 0x1e, 0x1e, 0x1e, + 0x17, 0x02, 0x21, 0x27, 0x1e, 0x22, 0x02, 0x2b, 0x33, 0x23, 0x2b, 0x09, + 0x26, 0x00, 0x02, 0x15, 0x26, 0x1c, 0x14, 0x02, 0x10, 0x4d, 0x10, 0x39, + 0x02, 0x15, 0x74, 0x10, 0x61, 0x02, 0x26, 0x9a, 0x1c, 0x87, 0x01, 0x19, + 0x9a, 0x02, 0x06, 0x73, 0x0d, 0x87, 0x02, 0x00, 0x4d, 0x00, 0x60, 0x02, + 0x06, 0x27, 0x00, 0x3a, 0x02, 0x19, 0x00, 0x0c, 0x14, 0x01, 0x26, 0x00, + 0x09, 0x00, 0x00, 0x01, 0x0e, 0x00, 0x02, 0x21, 0x27, 0x1b, 0x14, 0x02, + 0x28, 0x4d, 0x28, 0x3a, 0x02, 0x21, 0x73, 0x28, 0x60, 0x02, 0x0e, 0x9a, + 0x1b, 0x87, 0x01, 0x00, 0x9a, 0x02, 0x11, 0x74, 0x0c, 0x87, 0x02, 0x17, + 0x4d, 0x17, 0x61, 0x02, 0x11, 0x26, 0x17, 0x39, 0x02, 0x00, 0x00, 0x0c, + 0x14, 0x11, 0x4c, 0x17, 0x01, 0x2e, 0x27, 0x01, 0x4c, 0x38, 0x01, 0x48, + 0x40, 0x01, 0x2b, 0x2e, 0x01, 0x2b, 0x4e, 0x01, 0x22, 0x4e, 0x01, 0x22, + 0x2e, 0x01, 0x05, 0x40, 0x01, 0x00, 0x38, 0x01, 0x1e, 0x27, 0x01, 0x00, + 0x17, 0x01, 0x05, 0x0e, 0x01, 0x22, 0x1f, 0x01, 0x22, 0x00, 0x01, 0x2b, + 0x00, 0x01, 0x2b, 0x1f, 0x01, 0x48, 0x0e, 0x01, 0x4c, 0x17, 0x0b, 0x3e, + 0x00, 0x01, 0x3e, 0x30, 0x01, 0x6d, 0x30, 0x01, 0x6d, 0x3e, 0x01, 0x3e, + 0x3e, 0x01, 0x3e, 0x6d, 0x01, 0x30, 0x6d, 0x01, 0x30, 0x3e, 0x01, 0x00, + 0x3e, 0x01, 0x00, 0x30, 0x01, 0x30, 0x30, 0x01, 0x30, 0x00, 0x01, 0x3e, + 0x00, 0x05, 0x07, 0x00, 0x01, 0x19, 0x00, 0x01, 0x19, 0x0f, 0x01, 0x0b, + 0x2a, 0x01, 0x00, 0x2a, 0x01, 0x07, 0x0f, 0x01, 0x07, 0x00, 0x03, 0x00, + 0x00, 0x01, 0x2e, 0x00, 0x01, 0x2e, 0x0f, 0x01, 0x00, 0x0f, 0x01, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x01, 0x12, 0x00, 0x01, 0x12, 0x15, 0x01, 0x00, + 0x15, 0x01, 0x00, 0x00, 0x03, 0x2c, 0x00, 0x01, 0x3a, 0x00, 0x01, 0x0e, + 0x8e, 0x01, 0x00, 0x8e, 0x01, 0x2c, 0x00, 0x07, 0x1b, 0x00, 0x02, 0x07, + 0x0d, 0x0e, 0x00, 0x02, 0x00, 0x34, 0x00, 0x1a, 0x02, 0x07, 0x5c, 0x00, + 0x4f, 0x02, 0x1b, 0x69, 0x0e, 0x69, 0x02, 0x2f, 0x5c, 0x28, 0x69, 0x02, + 0x36, 0x34, 0x36, 0x4f, 0x02, 0x2f, 0x0d, 0x36, 0x1a, 0x02, 0x1b, 0x00, + 0x28, 0x00, 0x07, 0x2c, 0x00, 0x02, 0x4c, 0x11, 0x42, 0x00, 0x02, 0x58, + 0x41, 0x58, 0x21, 0x02, 0x4c, 0x72, 0x58, 0x61, 0x02, 0x2c, 0x83, 0x42, + 0x83, 0x02, 0x0b, 0x72, 0x17, 0x83, 0x02, 0x00, 0x41, 0x00, 0x61, 0x02, + 0x0b, 0x11, 0x00, 0x21, 0x02, 0x2c, 0x00, 0x17, 0x00, 0x0a, 0x03, 0x70, + 0x01, 0x1e, 0x70, 0x01, 0x1e, 0x0f, 0x01, 0x00, 0x15, 0x01, 0x00, 0x06, + 0x01, 0x1e, 0x00, 0x01, 0x2f, 0x00, 0x01, 0x2f, 0x70, 0x01, 0x4b, 0x70, + 0x01, 0x4b, 0x7e, 0x01, 0x03, 0x7e, 0x01, 0x03, 0x70, 0x14, 0x14, 0x72, + 0x01, 0x50, 0x72, 0x01, 0x50, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x72, + 0x02, 0x1a, 0x57, 0x09, 0x68, 0x02, 0x2f, 0x41, 0x2b, 0x46, 0x02, 0x3b, + 0x31, 0x37, 0x38, 0x02, 0x3e, 0x25, 0x3e, 0x2b, 0x02, 0x37, 0x14, 0x3e, + 0x1b, 0x02, 0x24, 0x0e, 0x30, 0x0e, 0x02, 0x13, 0x11, 0x1c, 0x0e, 0x02, + 0x00, 0x19, 0x0b, 0x13, 0x01, 0x00, 0x08, 0x02, 0x13, 0x02, 0x0b, 0x04, + 0x02, 0x24, 0x00, 0x1d, 0x00, 0x02, 0x44, 0x0a, 0x38, 0x00, 0x02, 0x4f, + 0x23, 0x4f, 0x13, 0x02, 0x4c, 0x32, 0x4f, 0x2c, 0x02, 0x42, 0x43, 0x4a, + 0x3a, 0x02, 0x34, 0x52, 0x40, 0x45, 0x02, 0x14, 0x72, 0x29, 0x5d, 0x1c, + 0x39, 0x3d, 0x02, 0x4c, 0x47, 0x46, 0x3f, 0x02, 0x53, 0x5c, 0x53, 0x50, + 0x02, 0x46, 0x79, 0x53, 0x6f, 0x02, 0x22, 0x83, 0x39, 0x83, 0x02, 0x11, + 0x82, 0x1a, 0x83, 0x02, 0x00, 0x7d, 0x09, 0x80, 0x01, 0x00, 0x6d, 0x02, + 0x0f, 0x73, 0x07, 0x71, 0x02, 0x22, 0x75, 0x18, 0x75, 0x02, 0x3a, 0x6f, + 0x31, 0x75, 0x02, 0x42, 0x5c, 0x42, 0x68, 0x02, 0x3b, 0x4a, 0x42, 0x51, + 0x02, 0x24, 0x44, 0x33, 0x44, 0x01, 0x16, 0x44, 0x01, 0x16, 0x36, 0x01, + 0x25, 0x36, 0x02, 0x39, 0x31, 0x32, 0x36, 0x02, 0x40, 0x22, 0x40, 0x2c, + 0x02, 0x39, 0x13, 0x40, 0x18, 0x02, 0x24, 0x0e, 0x31, 0x0e, 0x02, 0x15, + 0x0f, 0x1e, 0x0e, 0x02, 0x04, 0x15, 0x0d, 0x11, 0x01, 0x04, 0x05, 0x02, + 0x16, 0x01, 0x0d, 0x02, 0x02, 0x26, 0x00, 0x1f, 0x00, 0x02, 0x45, 0x08, + 0x39, 0x00, 0x02, 0x50, 0x21, 0x50, 0x11, 0x02, 0x4a, 0x32, 0x50, 0x2b, + 0x02, 0x39, 0x3d, 0x44, 0x39, 0x02, 0x2c, 0x00, 0x01, 0x00, 0x43, 0x01, + 0x2c, 0x43, 0x01, 0x2c, 0x00, 0x0a, 0x35, 0x00, 0x01, 0x4b, 0x00, 0x01, + 0x4b, 0x52, 0x01, 0x5c, 0x52, 0x01, 0x5c, 0x61, 0x01, 0x4b, 0x61, 0x01, + 0x4b, 0x7e, 0x01, 0x3a, 0x7e, 0x01, 0x3a, 0x61, 0x01, 0x00, 0x61, 0x01, + 0x00, 0x50, 0x01, 0x35, 0x00, 0x15, 0x06, 0x00, 0x01, 0x49, 0x00, 0x01, + 0x49, 0x0e, 0x01, 0x15, 0x0e, 0x01, 0x15, 0x2e, 0x02, 0x1d, 0x2c, 0x19, + 0x2c, 0x02, 0x24, 0x2b, 0x20, 0x2b, 0x02, 0x46, 0x37, 0x39, 0x2b, 0x02, + 0x52, 0x56, 0x52, 0x42, 0x02, 0x46, 0x76, 0x52, 0x6b, 0x02, 0x22, 0x81, + 0x39, 0x81, 0x02, 0x11, 0x80, 0x1a, 0x81, 0x02, 0x00, 0x7c, 0x09, 0x7e, + 0x01, 0x00, 0x6b, 0x02, 0x10, 0x71, 0x08, 0x6f, 0x02, 0x21, 0x73, 0x18, + 0x73, 0x02, 0x39, 0x6b, 0x30, 0x73, 0x02, 0x41, 0x56, 0x41, 0x63, 0x02, + 0x39, 0x41, 0x41, 0x49, 0x02, 0x21, 0x39, 0x30, 0x39, 0x02, 0x13, 0x3b, + 0x1a, 0x39, 0x02, 0x06, 0x3f, 0x0d, 0x3c, 0x01, 0x06, 0x00, 0x07, 0x19, + 0x00, 0x02, 0x07, 0x08, 0x0d, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x10, 0x02, + 0x07, 0x33, 0x00, 0x2b, 0x02, 0x19, 0x3c, 0x0d, 0x3c, 0x02, 0x2b, 0x33, + 0x24, 0x3c, 0x02, 0x32, 0x1e, 0x32, 0x2b, 0x02, 0x2b, 0x08, 0x32, 0x10, + 0x02, 0x19, 0x00, 0x24, 0x00, 0x10, 0x4f, 0x05, 0x01, 0x4f, 0x15, 0x02, + 0x42, 0x10, 0x49, 0x11, 0x02, 0x35, 0x0e, 0x3c, 0x0e, 0x02, 0x1b, 0x19, + 0x24, 0x0e, 0x02, 0x11, 0x3c, 0x12, 0x25, 0x02, 0x1d, 0x30, 0x16, 0x34, + 0x02, 0x2e, 0x2c, 0x25, 0x2c, 0x02, 0x4c, 0x38, 0x41, 0x2c, 0x02, 0x58, + 0x58, 0x58, 0x43, 0x02, 0x4c, 0x77, 0x58, 0x6b, 0x02, 0x2d, 0x83, 0x41, + 0x83, 0x02, 0x0c, 0x72, 0x17, 0x83, 0x02, 0x00, 0x41, 0x00, 0x61, 0x02, + 0x0e, 0x11, 0x00, 0x23, 0x02, 0x34, 0x00, 0x1d, 0x00, 0x02, 0x41, 0x01, + 0x3b, 0x00, 0x02, 0x4f, 0x05, 0x48, 0x02, 0x06, 0x00, 0x00, 0x01, 0x52, + 0x00, 0x01, 0x52, 0x07, 0x01, 0x23, 0x7e, 0x01, 0x12, 0x7e, 0x01, 0x3d, + 0x0e, 0x01, 0x00, 0x0e, 0x01, 0x00, 0x00, 0x07, 0x1a, 0x00, 0x02, 0x07, + 0x07, 0x0e, 0x00, 0x02, 0x00, 0x19, 0x00, 0x0d, 0x02, 0x07, 0x2b, 0x00, + 0x25, 0x02, 0x1a, 0x32, 0x0e, 0x32, 0x02, 0x2d, 0x2b, 0x26, 0x32, 0x02, + 0x34, 0x19, 0x34, 0x24, 0x02, 0x2d, 0x07, 0x34, 0x0d, 0x02, 0x1a, 0x00, + 0x26, 0x00, 0x0f, 0x1b, 0x3d, 0x02, 0x0a, 0x32, 0x10, 0x3a, 0x02, 0x04, + 0x20, 0x04, 0x2b, 0x02, 0x0f, 0x08, 0x04, 0x11, 0x02, 0x2c, 0x00, 0x19, + 0x00, 0x02, 0x4a, 0x08, 0x3f, 0x00, 0x02, 0x55, 0x20, 0x55, 0x11, 0x02, + 0x4e, 0x32, 0x55, 0x2b, 0x02, 0x3d, 0x3d, 0x48, 0x3a, 0x02, 0x50, 0x48, + 0x4a, 0x3f, 0x02, 0x57, 0x5c, 0x57, 0x50, 0x02, 0x4c, 0x79, 0x57, 0x6f, + 0x02, 0x2c, 0x83, 0x41, 0x83, 0x02, 0x0c, 0x79, 0x17, 0x83, 0x02, 0x00, + 0x5c, 0x00, 0x6f, 0x02, 0x08, 0x48, 0x00, 0x50, 0x02, 0x1b, 0x3d, 0x0f, + 0x3f, 0x07, 0x00, 0x15, 0x02, 0x06, 0x24, 0x00, 0x1f, 0x02, 0x17, 0x29, + 0x0c, 0x29, 0x02, 0x28, 0x24, 0x22, 0x29, 0x02, 0x2f, 0x15, 0x2f, 0x1f, + 0x02, 0x28, 0x06, 0x2f, 0x0b, 0x02, 0x17, 0x00, 0x22, 0x00, 0x02, 0x06, + 0x06, 0x0c, 0x00, 0x02, 0x00, 0x15, 0x00, 0x0b, 0x10, 0x08, 0x7e, 0x01, + 0x08, 0x6e, 0x02, 0x15, 0x73, 0x0f, 0x71, 0x02, 0x22, 0x75, 0x1c, 0x75, + 0x02, 0x3c, 0x69, 0x33, 0x75, 0x02, 0x46, 0x47, 0x45, 0x5e, 0x02, 0x39, + 0x52, 0x41, 0x4e, 0x02, 0x29, 0x56, 0x32, 0x56, 0x02, 0x0b, 0x4a, 0x16, + 0x56, 0x02, 0x00, 0x2b, 0x00, 0x3f, 0x02, 0x0b, 0x0b, 0x00, 0x17, 0x02, + 0x2a, 0x00, 0x17, 0x00, 0x02, 0x4c, 0x11, 0x40, 0x00, 0x02, 0x57, 0x41, + 0x57, 0x21, 0x02, 0x49, 0x71, 0x57, 0x5f, 0x02, 0x23, 0x83, 0x3b, 0x83, + 0x02, 0x15, 0x82, 0x1c, 0x83, 0x02, 0x08, 0x7e, 0x0f, 0x80, 0x07, 0x19, + 0x3b, 0x02, 0x2b, 0x33, 0x24, 0x3b, 0x02, 0x32, 0x1e, 0x32, 0x2c, 0x02, + 0x2b, 0x08, 0x32, 0x10, 0x02, 0x19, 0x00, 0x24, 0x00, 0x02, 0x06, 0x08, + 0x0d, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x10, 0x02, 0x06, 0x33, 0x00, 0x2c, + 0x02, 0x19, 0x3b, 0x0d, 0x3b, 0x03, 0x00, 0x00, 0x01, 0x12, 0x00, 0x01, + 0x12, 0x16, 0x01, 0x00, 0x16, 0x01, 0x00, 0x00, 0x06, 0x6d, 0x10, 0x01, + 0x15, 0x2f, 0x01, 0x6d, 0x4e, 0x01, 0x6d, 0x5d, 0x01, 0x00, 0x36, 0x01, + 0x00, 0x28, 0x01, 0x6d, 0x00, 0x01, 0x6d, 0x10, 0x03, 0x00, 0x00, 0x01, + 0x6d, 0x00, 0x01, 0x6d, 0x0e, 0x01, 0x00, 0x0e, 0x01, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x01, 0x6d, 0x00, 0x01, 0x6d, 0x0f, 0x01, 0x00, 0x0f, 0x01, + 0x00, 0x00, 0x06, 0x00, 0x10, 0x01, 0x00, 0x00, 0x01, 0x6d, 0x28, 0x01, + 0x6d, 0x36, 0x01, 0x00, 0x5d, 0x01, 0x00, 0x4e, 0x01, 0x58, 0x2f, 0x01, + 0x00, 0x10, 0x17, 0x25, 0x5f, 0x01, 0x16, 0x5f, 0x01, 0x16, 0x52, 0x02, + 0x18, 0x43, 0x16, 0x49, 0x02, 0x22, 0x36, 0x1b, 0x3e, 0x01, 0x29, 0x2f, + 0x02, 0x30, 0x27, 0x2e, 0x2b, 0x02, 0x33, 0x1f, 0x33, 0x23, 0x02, 0x2d, + 0x13, 0x33, 0x17, 0x02, 0x1f, 0x0e, 0x27, 0x0e, 0x02, 0x10, 0x11, 0x18, + 0x0e, 0x02, 0x00, 0x19, 0x08, 0x14, 0x01, 0x00, 0x09, 0x02, 0x10, 0x02, + 0x08, 0x04, 0x02, 0x21, 0x00, 0x18, 0x00, 0x02, 0x3a, 0x08, 0x31, 0x00, + 0x02, 0x44, 0x1d, 0x44, 0x10, 0x02, 0x41, 0x2a, 0x44, 0x24, 0x02, 0x36, + 0x37, 0x3e, 0x30, 0x01, 0x2f, 0x3e, 0x02, 0x29, 0x45, 0x2b, 0x42, 0x02, + 0x27, 0x49, 0x27, 0x47, 0x02, 0x26, 0x4d, 0x26, 0x4b, 0x02, 0x25, 0x54, + 0x25, 0x50, 0x01, 0x25, 0x5f, 0x07, 0x00, 0x1a, 0x02, 0x06, 0x2d, 0x00, + 0x26, 0x02, 0x17, 0x34, 0x0d, 0x34, 0x02, 0x28, 0x2d, 0x22, 0x34, 0x02, + 0x2d, 0x1a, 0x2d, 0x26, 0x02, 0x27, 0x07, 0x2d, 0x0e, 0x02, 0x17, 0x00, + 0x21, 0x00, 0x02, 0x06, 0x07, 0x0d, 0x00, 0x02, 0x00, 0x1a, 0x00, 0x0e, + 0x2c, 0x63, 0x66, 0x02, 0x58, 0x70, 0x5f, 0x6d, 0x02, 0x49, 0x73, 0x51, + 0x73, 0x02, 0x31, 0x69, 0x3a, 0x73, 0x02, 0x28, 0x4d, 0x28, 0x5e, 0x02, + 0x31, 0x32, 0x28, 0x3c, 0x02, 0x49, 0x27, 0x3a, 0x27, 0x02, 0x58, 0x2a, + 0x51, 0x27, 0x02, 0x63, 0x34, 0x5f, 0x2d, 0x01, 0x63, 0x29, 0x01, 0x70, + 0x29, 0x01, 0x70, 0x67, 0x02, 0x83, 0x5b, 0x7c, 0x65, 0x02, 0x8a, 0x43, + 0x8a, 0x52, 0x02, 0x87, 0x32, 0x8a, 0x3a, 0x02, 0x7f, 0x24, 0x85, 0x2a, + 0x02, 0x6a, 0x13, 0x76, 0x18, 0x02, 0x4e, 0x0d, 0x5d, 0x0d, 0x02, 0x3a, + 0x0f, 0x44, 0x0d, 0x02, 0x28, 0x17, 0x31, 0x12, 0x02, 0x13, 0x2e, 0x1c, + 0x20, 0x02, 0x0c, 0x4c, 0x0c, 0x3c, 0x02, 0x11, 0x66, 0x0c, 0x5a, 0x02, + 0x1f, 0x7a, 0x16, 0x72, 0x02, 0x34, 0x88, 0x28, 0x83, 0x02, 0x4d, 0x8d, + 0x3f, 0x8d, 0x02, 0x62, 0x89, 0x58, 0x8d, 0x02, 0x76, 0x7e, 0x6d, 0x85, + 0x01, 0x7d, 0x88, 0x02, 0x66, 0x95, 0x73, 0x90, 0x02, 0x4d, 0x99, 0x5a, + 0x99, 0x02, 0x2f, 0x94, 0x3d, 0x99, 0x02, 0x17, 0x83, 0x22, 0x8e, 0x02, + 0x06, 0x6b, 0x0c, 0x78, 0x02, 0x00, 0x4d, 0x00, 0x5d, 0x02, 0x06, 0x2f, + 0x00, 0x3d, 0x02, 0x17, 0x17, 0x0c, 0x21, 0x02, 0x30, 0x06, 0x22, 0x0c, + 0x02, 0x4e, 0x00, 0x3e, 0x00, 0x02, 0x70, 0x08, 0x60, 0x00, 0x02, 0x89, + 0x1d, 0x7f, 0x0f, 0x02, 0x93, 0x2e, 0x90, 0x25, 0x02, 0x96, 0x43, 0x96, + 0x39, 0x02, 0x89, 0x66, 0x96, 0x59, 0x02, 0x63, 0x74, 0x7b, 0x73, 0x01, + 0x63, 0x66, 0x02, 0x17, 0x00, 0x01, 0x00, 0x3f, 0x01, 0x2f, 0x3f, 0x01, + 0x17, 0x00, 0x07, 0x30, 0x00, 0x01, 0x44, 0x00, 0x01, 0x74, 0x7e, 0x01, + 0x63, 0x7e, 0x01, 0x57, 0x5e, 0x01, 0x1e, 0x5e, 0x01, 0x13, 0x7e, 0x01, + 0x00, 0x7e, 0x01, 0x30, 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x2e, 0x01, + 0x1c, 0x2e, 0x02, 0x30, 0x29, 0x29, 0x2e, 0x02, 0x37, 0x17, 0x37, 0x23, + 0x02, 0x30, 0x05, 0x37, 0x0c, 0x02, 0x1c, 0x00, 0x29, 0x00, 0x01, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x26, 0x01, 0x1a, 0x26, 0x02, 0x2c, + 0x21, 0x26, 0x26, 0x02, 0x32, 0x13, 0x32, 0x1c, 0x02, 0x2c, 0x05, 0x32, + 0x09, 0x02, 0x1a, 0x00, 0x26, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x01, 0x2c, 0x00, 0x02, 0x4a, 0x08, 0x3f, 0x00, 0x02, 0x55, 0x1f, 0x55, + 0x10, 0x02, 0x4f, 0x32, 0x55, 0x2a, 0x02, 0x3f, 0x3a, 0x4a, 0x39, 0x02, + 0x53, 0x45, 0x4c, 0x3d, 0x02, 0x59, 0x5b, 0x59, 0x4e, 0x02, 0x4e, 0x75, + 0x59, 0x6c, 0x02, 0x2d, 0x7e, 0x42, 0x7e, 0x01, 0x00, 0x7e, 0x01, 0x00, + 0x00, 0x11, 0x67, 0x0c, 0x01, 0x67, 0x1e, 0x02, 0x54, 0x12, 0x5e, 0x16, + 0x02, 0x3f, 0x0e, 0x4a, 0x0e, 0x02, 0x1e, 0x1b, 0x2a, 0x0e, 0x02, 0x13, + 0x41, 0x13, 0x28, 0x02, 0x1e, 0x67, 0x13, 0x5a, 0x02, 0x3f, 0x75, 0x2a, + 0x75, 0x02, 0x54, 0x71, 0x4a, 0x75, 0x02, 0x67, 0x65, 0x5e, 0x6d, 0x01, + 0x67, 0x77, 0x02, 0x54, 0x80, 0x5e, 0x7d, 0x02, 0x3e, 0x83, 0x4a, 0x83, + 0x02, 0x11, 0x71, 0x22, 0x83, 0x02, 0x00, 0x41, 0x00, 0x60, 0x02, 0x11, + 0x11, 0x00, 0x23, 0x02, 0x3e, 0x00, 0x22, 0x00, 0x02, 0x54, 0x03, 0x4a, + 0x00, 0x02, 0x67, 0x0c, 0x5e, 0x06, 0x06, 0x00, 0x00, 0x01, 0x00, 0x62, + 0x01, 0x15, 0x62, 0x02, 0x3b, 0x57, 0x2f, 0x62, 0x02, 0x47, 0x31, 0x47, + 0x4a, 0x02, 0x3b, 0x0b, 0x47, 0x18, 0x02, 0x15, 0x00, 0x2f, 0x00, 0x01, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x01, 0x23, 0x00, 0x02, 0x59, 0x0f, 0x48, + 0x00, 0x02, 0x6a, 0x3f, 0x6a, 0x1e, 0x02, 0x59, 0x6f, 0x6a, 0x60, 0x02, + 0x23, 0x7e, 0x48, 0x7e, 0x01, 0x00, 0x7e, 0x01, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x01, 0x50, 0x00, 0x01, 0x50, 0x0e, 0x01, 0x11, 0x0e, 0x01, 0x11, + 0x34, 0x01, 0x4d, 0x34, 0x01, 0x4d, 0x42, 0x01, 0x11, 0x42, 0x01, 0x11, + 0x70, 0x01, 0x51, 0x70, 0x01, 0x51, 0x7e, 0x01, 0x00, 0x7e, 0x01, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x01, 0x48, 0x00, 0x01, 0x48, 0x0e, 0x01, 0x11, + 0x0e, 0x01, 0x11, 0x34, 0x01, 0x43, 0x34, 0x01, 0x43, 0x42, 0x01, 0x11, + 0x42, 0x01, 0x11, 0x7e, 0x01, 0x00, 0x7e, 0x01, 0x00, 0x00, 0x15, 0x5e, + 0x6f, 0x01, 0x5e, 0x4d, 0x01, 0x42, 0x4d, 0x01, 0x42, 0x3f, 0x01, 0x6f, + 0x3f, 0x01, 0x6f, 0x75, 0x02, 0x59, 0x80, 0x65, 0x7c, 0x02, 0x3f, 0x83, + 0x4d, 0x83, 0x02, 0x11, 0x71, 0x22, 0x83, 0x02, 0x00, 0x41, 0x00, 0x61, + 0x02, 0x11, 0x11, 0x00, 0x22, 0x02, 0x3f, 0x00, 0x22, 0x00, 0x02, 0x57, + 0x03, 0x4c, 0x00, 0x02, 0x6c, 0x0c, 0x63, 0x06, 0x01, 0x6c, 0x1e, 0x02, + 0x58, 0x12, 0x62, 0x16, 0x02, 0x41, 0x0e, 0x4d, 0x0e, 0x02, 0x1e, 0x1b, + 0x2a, 0x0e, 0x02, 0x13, 0x41, 0x13, 0x28, 0x02, 0x1e, 0x68, 0x13, 0x5b, + 0x02, 0x41, 0x75, 0x2a, 0x75, 0x02, 0x51, 0x74, 0x4a, 0x75, 0x02, 0x5e, + 0x6f, 0x59, 0x71, 0x0b, 0x00, 0x00, 0x01, 0x11, 0x00, 0x01, 0x11, 0x34, + 0x01, 0x4f, 0x34, 0x01, 0x4f, 0x00, 0x01, 0x60, 0x00, 0x01, 0x60, 0x7e, + 0x01, 0x4f, 0x7e, 0x01, 0x4f, 0x42, 0x01, 0x11, 0x42, 0x01, 0x11, 0x7e, + 0x01, 0x00, 0x7e, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x11, 0x00, + 0x01, 0x11, 0x7e, 0x01, 0x00, 0x7e, 0x01, 0x00, 0x00, 0x09, 0x1a, 0x00, + 0x01, 0x2b, 0x00, 0x01, 0x2b, 0x76, 0x02, 0x22, 0x97, 0x2b, 0x8d, 0x02, + 0x06, 0xa1, 0x1a, 0xa1, 0x01, 0x00, 0xa1, 0x01, 0x00, 0x93, 0x01, 0x05, + 0x93, 0x02, 0x15, 0x8d, 0x10, 0x93, 0x02, 0x1a, 0x76, 0x1a, 0x86, 0x01, + 0x1a, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x11, 0x00, 0x01, 0x11, 0x35, 0x01, + 0x4a, 0x00, 0x01, 0x60, 0x00, 0x01, 0x21, 0x3b, 0x01, 0x64, 0x7e, 0x01, + 0x4e, 0x7e, 0x01, 0x11, 0x41, 0x01, 0x11, 0x7e, 0x01, 0x00, 0x7e, 0x01, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x11, 0x00, 0x01, 0x11, 0x70, 0x01, + 0x4f, 0x70, 0x01, 0x4f, 0x7e, 0x01, 0x00, 0x7e, 0x01, 0x00, 0x00, 0x0c, + 0x00, 0x00, 0x01, 0x1a, 0x00, 0x01, 0x39, 0x56, 0x01, 0x5a, 0x00, 0x01, + 0x74, 0x00, 0x01, 0x74, 0x7e, 0x01, 0x63, 0x7e, 0x01, 0x63, 0x0f, 0x01, + 0x42, 0x66, 0x01, 0x31, 0x66, 0x01, 0x11, 0x0f, 0x01, 0x11, 0x7e, 0x01, + 0x00, 0x7e, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x17, 0x00, 0x01, + 0x4f, 0x69, 0x01, 0x4f, 0x00, 0x01, 0x5f, 0x00, 0x01, 0x5f, 0x7e, 0x01, + 0x48, 0x7e, 0x01, 0x11, 0x15, 0x01, 0x11, 0x7e, 0x01, 0x00, 0x7e, 0x01, + 0x00, 0x00, 0x07, 0x28, 0x00, 0x02, 0x0b, 0x0d, 0x15, 0x00, 0x02, 0x00, + 0x33, 0x00, 0x1c, 0x02, 0x0b, 0x59, 0x00, 0x4b, 0x02, 0x28, 0x67, 0x15, + 0x67, 0x02, 0x46, 0x59, 0x3b, 0x67, 0x02, 0x50, 0x33, 0x50, 0x4b, 0x02, + 0x46, 0x0d, 0x50, 0x1c, 0x02, 0x28, 0x00, 0x3b, 0x00, 0x07, 0x3b, 0x00, + 0x02, 0x66, 0x11, 0x56, 0x00, 0x02, 0x76, 0x41, 0x76, 0x23, 0x02, 0x66, + 0x71, 0x76, 0x5f, 0x02, 0x3b, 0x83, 0x56, 0x83, 0x02, 0x11, 0x71, 0x20, + 0x83, 0x02, 0x00, 0x41, 0x00, 0x5f, 0x02, 0x11, 0x11, 0x00, 0x23, 0x02, + 0x3b, 0x00, 0x20, 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x2f, 0x01, 0x15, + 0x2f, 0x02, 0x28, 0x29, 0x22, 0x2f, 0x02, 0x2f, 0x18, 0x2f, 0x23, 0x02, + 0x28, 0x06, 0x2f, 0x0c, 0x02, 0x15, 0x00, 0x22, 0x00, 0x01, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x01, 0x26, 0x00, 0x02, 0x46, 0x09, 0x3c, 0x00, 0x02, + 0x52, 0x26, 0x52, 0x13, 0x02, 0x46, 0x42, 0x52, 0x39, 0x02, 0x26, 0x4c, + 0x3c, 0x4c, 0x01, 0x11, 0x4c, 0x01, 0x11, 0x7e, 0x01, 0x00, 0x7e, 0x01, + 0x00, 0x00, 0x0c, 0x53, 0x7e, 0x01, 0x69, 0x97, 0x01, 0x55, 0x97, 0x01, + 0x42, 0x82, 0x02, 0x3e, 0x83, 0x3f, 0x83, 0x02, 0x3b, 0x83, 0x3d, 0x83, + 0x02, 0x11, 0x71, 0x20, 0x83, 0x02, 0x00, 0x41, 0x00, 0x5f, 0x02, 0x11, + 0x11, 0x00, 0x23, 0x02, 0x3b, 0x00, 0x20, 0x00, 0x02, 0x66, 0x11, 0x56, + 0x00, 0x02, 0x76, 0x41, 0x76, 0x23, 0x02, 0x6d, 0x67, 0x76, 0x58, 0x02, + 0x53, 0x7e, 0x64, 0x77, 0x0f, 0x3c, 0x43, 0x02, 0x46, 0x4b, 0x42, 0x45, + 0x02, 0x51, 0x5c, 0x4c, 0x51, 0x01, 0x62, 0x7e, 0x01, 0x50, 0x7e, 0x01, + 0x40, 0x5e, 0x02, 0x33, 0x4d, 0x39, 0x51, 0x02, 0x24, 0x49, 0x2e, 0x49, + 0x01, 0x11, 0x49, 0x01, 0x11, 0x7e, 0x01, 0x00, 0x7e, 0x01, 0x00, 0x00, + 0x01, 0x26, 0x00, 0x02, 0x47, 0x08, 0x3c, 0x00, 0x02, 0x52, 0x24, 0x52, + 0x12, 0x02, 0x4c, 0x38, 0x52, 0x30, 0x02, 0x3c, 0x43, 0x46, 0x40, 0x06, + 0x00, 0x00, 0x01, 0x00, 0x2d, 0x01, 0x15, 0x2d, 0x02, 0x28, 0x27, 0x22, + 0x2d, 0x02, 0x2f, 0x16, 0x2f, 0x21, 0x02, 0x28, 0x05, 0x2f, 0x0b, 0x02, + 0x15, 0x00, 0x22, 0x00, 0x01, 0x00, 0x00, 0x1b, 0x52, 0x06, 0x01, 0x52, + 0x17, 0x02, 0x3f, 0x10, 0x48, 0x13, 0x02, 0x2f, 0x0e, 0x37, 0x0e, 0x02, + 0x19, 0x13, 0x21, 0x0e, 0x02, 0x11, 0x23, 0x11, 0x19, 0x02, 0x17, 0x30, + 0x11, 0x2b, 0x02, 0x2a, 0x36, 0x1c, 0x34, 0x01, 0x34, 0x39, 0x02, 0x50, + 0x45, 0x47, 0x3c, 0x02, 0x59, 0x5e, 0x59, 0x4e, 0x02, 0x4d, 0x7a, 0x59, + 0x70, 0x02, 0x29, 0x83, 0x41, 0x83, 0x02, 0x15, 0x81, 0x20, 0x83, 0x02, + 0x01, 0x7b, 0x0c, 0x7f, 0x01, 0x01, 0x69, 0x02, 0x15, 0x72, 0x0b, 0x6f, + 0x02, 0x29, 0x75, 0x1f, 0x75, 0x02, 0x3f, 0x6f, 0x37, 0x75, 0x02, 0x48, + 0x5f, 0x48, 0x69, 0x02, 0x42, 0x50, 0x48, 0x56, 0x02, 0x2f, 0x49, 0x3c, + 0x4c, 0x01, 0x24, 0x47, 0x02, 0x09, 0x3b, 0x11, 0x43, 0x02, 0x00, 0x24, + 0x00, 0x32, 0x02, 0x0c, 0x09, 0x00, 0x13, 0x02, 0x2c, 0x00, 0x18, 0x00, + 0x02, 0x3f, 0x02, 0x35, 0x00, 0x02, 0x52, 0x06, 0x48, 0x03, 0x07, 0x00, + 0x00, 0x01, 0x6b, 0x00, 0x01, 0x6b, 0x0e, 0x01, 0x3f, 0x0e, 0x01, 0x3f, + 0x7e, 0x01, 0x2d, 0x7e, 0x01, 0x2d, 0x0e, 0x01, 0x00, 0x0e, 0x01, 0x00, + 0x00, 0x0d, 0x00, 0x00, 0x01, 0x11, 0x00, 0x01, 0x11, 0x4d, 0x02, 0x18, + 0x6a, 0x11, 0x61, 0x02, 0x31, 0x73, 0x20, 0x73, 0x02, 0x48, 0x6a, 0x41, + 0x73, 0x02, 0x50, 0x4d, 0x50, 0x61, 0x01, 0x50, 0x00, 0x01, 0x61, 0x00, + 0x01, 0x61, 0x4f, 0x02, 0x55, 0x74, 0x61, 0x67, 0x02, 0x31, 0x81, 0x48, + 0x81, 0x02, 0x0c, 0x74, 0x18, 0x81, 0x02, 0x00, 0x4f, 0x00, 0x67, 0x01, + 0x00, 0x00, 0x06, 0x30, 0x7e, 0x01, 0x00, 0x00, 0x01, 0x12, 0x00, 0x01, + 0x3a, 0x6a, 0x01, 0x63, 0x00, 0x01, 0x74, 0x00, 0x01, 0x44, 0x7e, 0x01, + 0x30, 0x7e, 0x0c, 0x00, 0x00, 0x01, 0x12, 0x00, 0x01, 0x2c, 0x6b, 0x01, + 0x47, 0x00, 0x01, 0x5b, 0x00, 0x01, 0x75, 0x6b, 0x01, 0x8f, 0x00, 0x01, + 0xa1, 0x00, 0x01, 0x81, 0x7e, 0x01, 0x6b, 0x7e, 0x01, 0x51, 0x11, 0x01, + 0x36, 0x7e, 0x01, 0x20, 0x7e, 0x01, 0x00, 0x00, 0x0b, 0x06, 0x00, 0x01, + 0x18, 0x00, 0x01, 0x38, 0x2f, 0x01, 0x57, 0x00, 0x01, 0x69, 0x00, 0x01, + 0x41, 0x3d, 0x01, 0x6c, 0x7e, 0x01, 0x5a, 0x7e, 0x01, 0x37, 0x48, 0x01, + 0x13, 0x7e, 0x01, 0x00, 0x7e, 0x01, 0x2d, 0x3b, 0x01, 0x06, 0x00, 0x08, + 0x00, 0x00, 0x01, 0x13, 0x00, 0x01, 0x36, 0x34, 0x01, 0x59, 0x00, 0x01, + 0x6b, 0x00, 0x01, 0x3f, 0x42, 0x01, 0x3f, 0x7e, 0x01, 0x2d, 0x7e, 0x01, + 0x2d, 0x42, 0x01, 0x00, 0x00, 0x09, 0x02, 0x00, 0x01, 0x66, 0x00, 0x01, + 0x66, 0x0c, 0x01, 0x16, 0x70, 0x01, 0x68, 0x70, 0x01, 0x68, 0x7e, 0x01, + 0x00, 0x7e, 0x01, 0x00, 0x72, 0x01, 0x50, 0x0e, 0x01, 0x02, 0x0e, 0x01, + 0x02, 0x00, 0x07, 0x00, 0x00, 0x01, 0x24, 0x00, 0x01, 0x24, 0x0c, 0x01, + 0x0f, 0x0c, 0x01, 0x0f, 0x8f, 0x01, 0x24, 0x8f, 0x01, 0x24, 0x9b, 0x01, + 0x00, 0x9b, 0x01, 0x00, 0x00, 0x03, 0x0e, 0x00, 0x01, 0x3a, 0x8e, 0x01, + 0x2c, 0x8e, 0x01, 0x00, 0x00, 0x01, 0x0e, 0x00, 0x07, 0x24, 0x00, 0x01, + 0x24, 0x9b, 0x01, 0x00, 0x9b, 0x01, 0x00, 0x8f, 0x01, 0x14, 0x8f, 0x01, + 0x14, 0x0c, 0x01, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x01, 0x24, 0x00, 0x06, + 0x3f, 0x00, 0x01, 0x6d, 0x2f, 0x01, 0x5c, 0x2f, 0x01, 0x36, 0x0d, 0x01, + 0x11, 0x2f, 0x01, 0x00, 0x2f, 0x01, 0x2e, 0x00, 0x01, 0x3f, 0x00, 0x03, + 0x5a, 0x00, 0x01, 0x5a, 0x0c, 0x01, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x01, + 0x5a, 0x00, 0x03, 0x11, 0x00, 0x01, 0x29, 0x20, 0x01, 0x1c, 0x20, 0x01, + 0x00, 0x00, 0x01, 0x11, 0x00, 0x07, 0x22, 0x00, 0x02, 0x07, 0x04, 0x0f, + 0x00, 0x02, 0x00, 0x13, 0x00, 0x09, 0x02, 0x05, 0x20, 0x00, 0x1c, 0x02, + 0x14, 0x25, 0x0a, 0x25, 0x02, 0x29, 0x1c, 0x21, 0x25, 0x02, 0x31, 0x03, + 0x31, 0x13, 0x01, 0x31, 0x00, 0x01, 0x22, 0x00, 0x13, 0x51, 0x2c, 0x01, + 0x51, 0x61, 0x01, 0x41, 0x61, 0x01, 0x41, 0x53, 0x02, 0x34, 0x60, 0x3c, + 0x5c, 0x02, 0x20, 0x64, 0x2b, 0x64, 0x02, 0x09, 0x5c, 0x12, 0x64, 0x02, + 0x00, 0x46, 0x00, 0x54, 0x02, 0x0b, 0x2e, 0x00, 0x36, 0x02, 0x2b, 0x26, + 0x16, 0x26, 0x01, 0x41, 0x26, 0x01, 0x41, 0x24, 0x02, 0x3a, 0x14, 0x41, + 0x1a, 0x02, 0x26, 0x0e, 0x33, 0x0e, 0x02, 0x16, 0x10, 0x1e, 0x0e, 0x02, + 0x08, 0x16, 0x0e, 0x12, 0x01, 0x08, 0x08, 0x02, 0x18, 0x02, 0x10, 0x04, + 0x02, 0x27, 0x00, 0x20, 0x00, 0x02, 0x47, 0x0b, 0x3c, 0x00, 0x02, 0x51, + 0x2c, 0x51, 0x16, 0x07, 0x36, 0x26, 0x02, 0x2e, 0x0a, 0x36, 0x14, 0x02, + 0x1b, 0x00, 0x27, 0x00, 0x02, 0x08, 0x0a, 0x0e, 0x00, 0x02, 0x00, 0x26, + 0x00, 0x14, 0x02, 0x08, 0x41, 0x00, 0x37, 0x02, 0x1b, 0x4a, 0x0e, 0x4a, + 0x02, 0x2e, 0x41, 0x27, 0x4a, 0x02, 0x36, 0x26, 0x36, 0x37, 0x0c, 0x10, + 0x34, 0x02, 0x1d, 0x27, 0x15, 0x2c, 0x02, 0x2f, 0x23, 0x24, 0x23, 0x02, + 0x4a, 0x31, 0x40, 0x23, 0x02, 0x55, 0x56, 0x55, 0x3f, 0x02, 0x4a, 0x7a, + 0x55, 0x6c, 0x02, 0x2f, 0x87, 0x40, 0x87, 0x02, 0x1d, 0x83, 0x24, 0x87, + 0x02, 0x10, 0x76, 0x15, 0x7f, 0x01, 0x10, 0x84, 0x01, 0x00, 0x84, 0x01, + 0x00, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x34, 0x11, 0x4c, 0x07, 0x01, + 0x4c, 0x15, 0x02, 0x3e, 0x10, 0x45, 0x12, 0x02, 0x31, 0x0e, 0x37, 0x0e, + 0x02, 0x19, 0x17, 0x22, 0x0e, 0x02, 0x11, 0x33, 0x11, 0x21, 0x02, 0x19, + 0x4d, 0x11, 0x44, 0x02, 0x31, 0x57, 0x22, 0x57, 0x02, 0x3e, 0x55, 0x37, + 0x57, 0x02, 0x4c, 0x4f, 0x45, 0x53, 0x01, 0x4c, 0x5d, 0x02, 0x3e, 0x63, + 0x45, 0x61, 0x02, 0x2f, 0x64, 0x37, 0x64, 0x02, 0x0d, 0x57, 0x19, 0x64, + 0x02, 0x00, 0x33, 0x00, 0x49, 0x02, 0x0d, 0x0e, 0x00, 0x1b, 0x02, 0x30, + 0x00, 0x1a, 0x00, 0x02, 0x3e, 0x02, 0x37, 0x00, 0x02, 0x4c, 0x07, 0x45, + 0x04, 0x0c, 0x46, 0x34, 0x01, 0x46, 0x00, 0x01, 0x55, 0x00, 0x01, 0x55, + 0x84, 0x01, 0x46, 0x84, 0x01, 0x46, 0x76, 0x02, 0x39, 0x83, 0x41, 0x7f, + 0x02, 0x27, 0x87, 0x32, 0x87, 0x02, 0x0b, 0x7a, 0x16, 0x87, 0x02, 0x00, + 0x56, 0x00, 0x6c, 0x02, 0x0b, 0x31, 0x00, 0x3f, 0x02, 0x27, 0x23, 0x16, + 0x23, 0x02, 0x39, 0x27, 0x32, 0x23, 0x02, 0x46, 0x34, 0x41, 0x2c, 0x07, + 0x00, 0x26, 0x02, 0x06, 0x41, 0x00, 0x37, 0x02, 0x1a, 0x4a, 0x0e, 0x4a, + 0x02, 0x2e, 0x41, 0x26, 0x4a, 0x02, 0x35, 0x26, 0x35, 0x37, 0x02, 0x2e, + 0x0a, 0x35, 0x14, 0x02, 0x1a, 0x00, 0x26, 0x00, 0x02, 0x06, 0x0a, 0x0e, + 0x00, 0x02, 0x00, 0x26, 0x00, 0x14, 0x0e, 0x59, 0x2f, 0x01, 0x59, 0x36, + 0x01, 0x11, 0x36, 0x02, 0x1a, 0x4e, 0x12, 0x46, 0x02, 0x33, 0x57, 0x23, + 0x57, 0x02, 0x44, 0x55, 0x3b, 0x57, 0x02, 0x54, 0x4e, 0x4c, 0x52, 0x01, + 0x54, 0x5d, 0x02, 0x44, 0x62, 0x4c, 0x60, 0x02, 0x32, 0x64, 0x3b, 0x64, + 0x02, 0x0e, 0x57, 0x1b, 0x64, 0x02, 0x00, 0x33, 0x00, 0x4a, 0x02, 0x0d, + 0x0e, 0x00, 0x1c, 0x02, 0x2f, 0x00, 0x19, 0x00, 0x02, 0x4d, 0x0d, 0x42, + 0x00, 0x02, 0x59, 0x2f, 0x59, 0x19, 0x04, 0x38, 0x1c, 0x02, 0x30, 0x07, + 0x38, 0x0f, 0x02, 0x1e, 0x00, 0x2a, 0x00, 0x02, 0x09, 0x07, 0x11, 0x00, + 0x02, 0x00, 0x1c, 0x02, 0x0f, 0x01, 0x38, 0x1c, 0x11, 0x3c, 0x00, 0x01, + 0x3c, 0x0d, 0x01, 0x2d, 0x0d, 0x02, 0x22, 0x10, 0x25, 0x0d, 0x02, 0x1e, + 0x1d, 0x1e, 0x14, 0x01, 0x1e, 0x25, 0x01, 0x38, 0x25, 0x01, 0x38, 0x32, + 0x01, 0x1e, 0x32, 0x01, 0x1e, 0x84, 0x01, 0x0f, 0x84, 0x01, 0x0f, 0x32, + 0x01, 0x00, 0x32, 0x01, 0x00, 0x25, 0x01, 0x0f, 0x25, 0x01, 0x0f, 0x1f, + 0x02, 0x16, 0x08, 0x0f, 0x0f, 0x02, 0x2d, 0x00, 0x1e, 0x00, 0x01, 0x3c, + 0x00, 0x07, 0x35, 0x24, 0x02, 0x2e, 0x0a, 0x35, 0x13, 0x02, 0x1a, 0x00, + 0x27, 0x00, 0x02, 0x06, 0x0a, 0x0d, 0x00, 0x02, 0x00, 0x24, 0x00, 0x13, + 0x02, 0x06, 0x3e, 0x00, 0x35, 0x02, 0x1a, 0x48, 0x0d, 0x48, 0x02, 0x2e, + 0x3e, 0x27, 0x48, 0x02, 0x35, 0x24, 0x35, 0x35, 0x14, 0x55, 0x56, 0x02, + 0x4a, 0x7a, 0x55, 0x6e, 0x02, 0x2a, 0x85, 0x3f, 0x85, 0x02, 0x1a, 0x84, + 0x22, 0x85, 0x02, 0x0c, 0x80, 0x13, 0x83, 0x01, 0x0c, 0x71, 0x02, 0x19, + 0x76, 0x13, 0x75, 0x02, 0x27, 0x78, 0x20, 0x78, 0x02, 0x3e, 0x70, 0x37, + 0x78, 0x02, 0x46, 0x59, 0x46, 0x69, 0x01, 0x46, 0x50, 0x02, 0x39, 0x5d, + 0x41, 0x59, 0x02, 0x27, 0x61, 0x32, 0x61, 0x02, 0x0b, 0x54, 0x16, 0x61, + 0x02, 0x00, 0x31, 0x00, 0x47, 0x02, 0x0b, 0x0e, 0x00, 0x1b, 0x02, 0x27, + 0x00, 0x16, 0x00, 0x02, 0x39, 0x04, 0x32, 0x00, 0x02, 0x46, 0x11, 0x41, + 0x09, 0x01, 0x46, 0x02, 0x01, 0x55, 0x02, 0x01, 0x55, 0x56, 0x0f, 0x50, + 0x4b, 0x01, 0x50, 0x84, 0x01, 0x40, 0x84, 0x01, 0x40, 0x4b, 0x02, 0x3b, + 0x38, 0x40, 0x3f, 0x02, 0x2b, 0x31, 0x36, 0x31, 0x02, 0x18, 0x39, 0x1f, + 0x31, 0x02, 0x10, 0x4f, 0x10, 0x41, 0x01, 0x10, 0x84, 0x01, 0x00, 0x84, + 0x01, 0x00, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x34, 0x02, 0x1e, 0x27, + 0x16, 0x2c, 0x02, 0x2f, 0x23, 0x25, 0x23, 0x02, 0x48, 0x2e, 0x40, 0x23, + 0x02, 0x50, 0x4b, 0x50, 0x38, 0x03, 0x00, 0x00, 0x01, 0x10, 0x00, 0x01, + 0x10, 0x5f, 0x01, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, + 0x10, 0x00, 0x01, 0x10, 0x14, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x09, + 0x14, 0x00, 0x01, 0x24, 0x00, 0x01, 0x24, 0x61, 0x02, 0x1d, 0x7b, 0x24, + 0x73, 0x02, 0x07, 0x83, 0x16, 0x83, 0x01, 0x00, 0x83, 0x01, 0x00, 0x76, + 0x01, 0x05, 0x76, 0x02, 0x11, 0x72, 0x0e, 0x76, 0x02, 0x14, 0x61, 0x14, + 0x6e, 0x01, 0x14, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, + 0x4e, 0x01, 0x3f, 0x25, 0x01, 0x53, 0x25, 0x01, 0x20, 0x52, 0x01, 0x55, + 0x84, 0x01, 0x40, 0x84, 0x01, 0x10, 0x56, 0x01, 0x10, 0x84, 0x01, 0x00, + 0x84, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, + 0x84, 0x01, 0x00, 0x84, 0x01, 0x00, 0x00, 0x1a, 0x4b, 0x15, 0x02, 0x59, + 0x05, 0x51, 0x0b, 0x02, 0x6c, 0x00, 0x61, 0x00, 0x02, 0x83, 0x0b, 0x7b, + 0x00, 0x02, 0x8c, 0x28, 0x8c, 0x15, 0x01, 0x8c, 0x61, 0x01, 0x7b, 0x61, + 0x01, 0x7b, 0x28, 0x02, 0x76, 0x15, 0x7b, 0x1b, 0x02, 0x68, 0x0e, 0x72, + 0x0e, 0x02, 0x55, 0x16, 0x5c, 0x0e, 0x02, 0x4e, 0x2c, 0x4e, 0x1e, 0x01, + 0x4e, 0x61, 0x01, 0x3e, 0x61, 0x01, 0x3e, 0x28, 0x02, 0x39, 0x15, 0x3e, + 0x1b, 0x02, 0x2b, 0x0e, 0x35, 0x0e, 0x02, 0x18, 0x16, 0x1e, 0x0e, 0x02, + 0x10, 0x2c, 0x10, 0x1e, 0x01, 0x10, 0x61, 0x01, 0x00, 0x61, 0x01, 0x00, + 0x02, 0x01, 0x10, 0x02, 0x01, 0x10, 0x11, 0x02, 0x1d, 0x04, 0x15, 0x09, + 0x02, 0x2f, 0x00, 0x24, 0x00, 0x02, 0x40, 0x06, 0x39, 0x00, 0x02, 0x4b, + 0x15, 0x48, 0x0b, 0x0f, 0x50, 0x28, 0x01, 0x50, 0x61, 0x01, 0x40, 0x61, + 0x01, 0x40, 0x28, 0x02, 0x3b, 0x15, 0x40, 0x1c, 0x02, 0x2b, 0x0e, 0x36, + 0x0e, 0x02, 0x18, 0x16, 0x1f, 0x0e, 0x02, 0x10, 0x2c, 0x10, 0x1e, 0x01, + 0x10, 0x61, 0x01, 0x00, 0x61, 0x01, 0x00, 0x02, 0x01, 0x10, 0x02, 0x01, + 0x10, 0x11, 0x02, 0x1e, 0x04, 0x16, 0x09, 0x02, 0x2f, 0x00, 0x25, 0x00, + 0x02, 0x48, 0x0b, 0x40, 0x00, 0x02, 0x50, 0x28, 0x50, 0x15, 0x07, 0x1b, + 0x00, 0x02, 0x07, 0x09, 0x0f, 0x00, 0x02, 0x00, 0x25, 0x00, 0x14, 0x02, + 0x07, 0x3f, 0x00, 0x35, 0x02, 0x1b, 0x49, 0x0f, 0x49, 0x02, 0x2e, 0x3f, + 0x28, 0x49, 0x02, 0x36, 0x25, 0x36, 0x35, 0x02, 0x2e, 0x09, 0x36, 0x14, + 0x02, 0x1b, 0x00, 0x28, 0x00, 0x07, 0x2c, 0x00, 0x02, 0x4c, 0x0e, 0x40, + 0x00, 0x02, 0x58, 0x33, 0x58, 0x1b, 0x02, 0x4c, 0x57, 0x58, 0x4a, 0x02, + 0x2c, 0x64, 0x40, 0x64, 0x02, 0x0c, 0x57, 0x17, 0x64, 0x02, 0x00, 0x33, + 0x00, 0x4a, 0x02, 0x0c, 0x0e, 0x00, 0x1b, 0x02, 0x2c, 0x00, 0x17, 0x00, + 0x0c, 0x10, 0x53, 0x01, 0x10, 0x85, 0x01, 0x00, 0x85, 0x01, 0x00, 0x02, + 0x01, 0x10, 0x02, 0x01, 0x10, 0x11, 0x02, 0x1d, 0x04, 0x15, 0x09, 0x02, + 0x2f, 0x00, 0x24, 0x00, 0x02, 0x4a, 0x0e, 0x40, 0x00, 0x02, 0x55, 0x33, + 0x55, 0x1c, 0x02, 0x4a, 0x57, 0x55, 0x49, 0x02, 0x2f, 0x64, 0x40, 0x64, + 0x02, 0x1d, 0x60, 0x24, 0x64, 0x02, 0x10, 0x53, 0x15, 0x5c, 0x0c, 0x46, + 0x53, 0x02, 0x39, 0x60, 0x41, 0x5c, 0x02, 0x27, 0x64, 0x32, 0x64, 0x02, + 0x0b, 0x57, 0x16, 0x64, 0x02, 0x00, 0x33, 0x00, 0x49, 0x02, 0x0b, 0x0e, + 0x00, 0x1c, 0x02, 0x27, 0x00, 0x16, 0x00, 0x02, 0x39, 0x04, 0x32, 0x00, + 0x02, 0x46, 0x11, 0x41, 0x09, 0x01, 0x46, 0x02, 0x01, 0x55, 0x02, 0x01, + 0x55, 0x85, 0x01, 0x46, 0x85, 0x01, 0x46, 0x53, 0x0d, 0x38, 0x11, 0x02, + 0x33, 0x0f, 0x35, 0x10, 0x02, 0x2c, 0x0f, 0x2f, 0x0f, 0x02, 0x18, 0x17, + 0x1e, 0x0f, 0x02, 0x10, 0x2f, 0x10, 0x20, 0x01, 0x10, 0x61, 0x01, 0x00, + 0x61, 0x01, 0x00, 0x02, 0x01, 0x10, 0x02, 0x01, 0x10, 0x11, 0x02, 0x1d, + 0x04, 0x15, 0x09, 0x02, 0x30, 0x00, 0x25, 0x00, 0x02, 0x34, 0x00, 0x32, + 0x00, 0x02, 0x38, 0x01, 0x36, 0x01, 0x01, 0x38, 0x11, 0x1b, 0x44, 0x05, + 0x01, 0x44, 0x14, 0x02, 0x36, 0x0f, 0x3d, 0x11, 0x02, 0x27, 0x0d, 0x2f, + 0x0d, 0x02, 0x16, 0x11, 0x1b, 0x0d, 0x02, 0x10, 0x1c, 0x10, 0x15, 0x02, + 0x14, 0x24, 0x10, 0x21, 0x02, 0x25, 0x2a, 0x18, 0x27, 0x01, 0x2a, 0x2b, + 0x02, 0x41, 0x35, 0x3b, 0x2f, 0x02, 0x49, 0x48, 0x49, 0x3c, 0x02, 0x3e, + 0x5d, 0x49, 0x55, 0x02, 0x22, 0x64, 0x34, 0x64, 0x02, 0x11, 0x63, 0x1a, + 0x64, 0x02, 0x00, 0x5f, 0x09, 0x61, 0x01, 0x00, 0x4e, 0x02, 0x11, 0x55, + 0x09, 0x53, 0x02, 0x22, 0x57, 0x19, 0x57, 0x02, 0x33, 0x53, 0x2d, 0x57, + 0x02, 0x39, 0x49, 0x39, 0x50, 0x02, 0x35, 0x3f, 0x39, 0x42, 0x02, 0x22, + 0x39, 0x31, 0x3b, 0x01, 0x1c, 0x37, 0x02, 0x07, 0x2e, 0x0e, 0x34, 0x02, + 0x01, 0x1c, 0x01, 0x28, 0x02, 0x0b, 0x08, 0x01, 0x0f, 0x02, 0x26, 0x00, + 0x14, 0x00, 0x02, 0x36, 0x02, 0x2e, 0x00, 0x02, 0x44, 0x05, 0x3d, 0x03, + 0x11, 0x1b, 0x00, 0x01, 0x1b, 0x1b, 0x01, 0x3b, 0x1b, 0x01, 0x3b, 0x28, + 0x01, 0x1b, 0x28, 0x01, 0x1b, 0x5b, 0x02, 0x1d, 0x6a, 0x1b, 0x67, 0x02, + 0x2b, 0x6e, 0x21, 0x6e, 0x01, 0x3b, 0x6e, 0x01, 0x3b, 0x7a, 0x01, 0x2b, + 0x7a, 0x02, 0x12, 0x74, 0x19, 0x7a, 0x02, 0x0b, 0x5b, 0x0b, 0x6d, 0x01, + 0x0b, 0x28, 0x01, 0x00, 0x28, 0x01, 0x00, 0x1b, 0x01, 0x0b, 0x1b, 0x01, + 0x0b, 0x00, 0x01, 0x1b, 0x00, 0x0f, 0x00, 0x3a, 0x01, 0x00, 0x00, 0x01, + 0x0f, 0x00, 0x01, 0x0f, 0x39, 0x02, 0x14, 0x4e, 0x0f, 0x47, 0x02, 0x24, + 0x55, 0x1a, 0x55, 0x02, 0x38, 0x4c, 0x31, 0x55, 0x02, 0x40, 0x37, 0x40, + 0x44, 0x01, 0x40, 0x00, 0x01, 0x4f, 0x00, 0x01, 0x4f, 0x5f, 0x01, 0x40, + 0x5f, 0x01, 0x40, 0x51, 0x02, 0x32, 0x5e, 0x3a, 0x5a, 0x02, 0x21, 0x62, + 0x2b, 0x62, 0x02, 0x08, 0x58, 0x11, 0x62, 0x02, 0x00, 0x3a, 0x00, 0x4e, + 0x06, 0x00, 0x00, 0x01, 0x11, 0x00, 0x01, 0x2e, 0x50, 0x01, 0x4c, 0x00, + 0x01, 0x5d, 0x00, 0x01, 0x39, 0x5f, 0x01, 0x24, 0x5f, 0x01, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x01, 0x10, 0x00, 0x01, 0x23, 0x4a, 0x01, 0x37, 0x00, + 0x01, 0x49, 0x00, 0x01, 0x5d, 0x4a, 0x01, 0x70, 0x00, 0x01, 0x80, 0x00, + 0x01, 0x67, 0x5f, 0x01, 0x54, 0x5f, 0x01, 0x40, 0x11, 0x01, 0x2b, 0x5f, + 0x01, 0x19, 0x5f, 0x01, 0x00, 0x00, 0x0b, 0x5a, 0x00, 0x01, 0x38, 0x2f, + 0x01, 0x5c, 0x5f, 0x01, 0x4a, 0x5f, 0x01, 0x2e, 0x3a, 0x01, 0x12, 0x5f, + 0x01, 0x00, 0x5f, 0x01, 0x25, 0x2e, 0x01, 0x03, 0x00, 0x01, 0x15, 0x00, + 0x01, 0x2e, 0x22, 0x01, 0x48, 0x00, 0x01, 0x5a, 0x00, 0x0d, 0x32, 0x68, + 0x02, 0x26, 0x7e, 0x2c, 0x79, 0x02, 0x15, 0x83, 0x1f, 0x83, 0x01, 0x08, + 0x83, 0x01, 0x08, 0x76, 0x01, 0x12, 0x76, 0x02, 0x1b, 0x74, 0x18, 0x76, + 0x02, 0x24, 0x65, 0x1f, 0x70, 0x01, 0x26, 0x5e, 0x01, 0x00, 0x00, 0x01, + 0x11, 0x00, 0x01, 0x2e, 0x4b, 0x01, 0x4c, 0x00, 0x01, 0x5d, 0x00, 0x01, + 0x32, 0x68, 0x09, 0x02, 0x00, 0x01, 0x4c, 0x00, 0x01, 0x4c, 0x0f, 0x01, + 0x12, 0x53, 0x01, 0x4c, 0x53, 0x01, 0x4c, 0x5f, 0x01, 0x00, 0x5f, 0x01, + 0x00, 0x51, 0x01, 0x3b, 0x0d, 0x01, 0x02, 0x0d, 0x01, 0x02, 0x00, 0x1c, + 0x43, 0x94, 0x01, 0x43, 0xa0, 0x01, 0x3d, 0xa0, 0x02, 0x21, 0x9a, 0x28, + 0xa0, 0x02, 0x1a, 0x81, 0x1a, 0x94, 0x01, 0x1a, 0x6d, 0x02, 0x15, 0x5b, + 0x1a, 0x60, 0x02, 0x05, 0x56, 0x11, 0x56, 0x01, 0x00, 0x56, 0x01, 0x00, + 0x4a, 0x01, 0x05, 0x4a, 0x02, 0x15, 0x45, 0x11, 0x4a, 0x02, 0x1a, 0x34, + 0x1a, 0x41, 0x01, 0x1a, 0x1f, 0x02, 0x21, 0x06, 0x1a, 0x0c, 0x02, 0x3d, + 0x00, 0x28, 0x00, 0x01, 0x43, 0x00, 0x01, 0x43, 0x0c, 0x01, 0x3d, 0x0c, + 0x02, 0x2d, 0x10, 0x31, 0x0c, 0x02, 0x2a, 0x20, 0x2a, 0x14, 0x01, 0x2a, + 0x35, 0x02, 0x26, 0x48, 0x2a, 0x42, 0x02, 0x19, 0x50, 0x22, 0x4e, 0x02, + 0x26, 0x58, 0x22, 0x52, 0x02, 0x2a, 0x6c, 0x2a, 0x5e, 0x01, 0x2a, 0x81, + 0x02, 0x2d, 0x91, 0x2a, 0x8d, 0x02, 0x3d, 0x94, 0x31, 0x94, 0x01, 0x43, + 0x94, 0x03, 0x0e, 0x00, 0x01, 0x0e, 0xae, 0x01, 0x00, 0xae, 0x01, 0x00, + 0x00, 0x01, 0x0e, 0x00, 0x1c, 0x00, 0x94, 0x01, 0x06, 0x94, 0x02, 0x15, + 0x91, 0x11, 0x94, 0x02, 0x19, 0x81, 0x19, 0x8d, 0x01, 0x19, 0x6c, 0x02, + 0x1c, 0x58, 0x19, 0x5e, 0x02, 0x2a, 0x50, 0x20, 0x52, 0x02, 0x1c, 0x48, + 0x20, 0x4e, 0x02, 0x19, 0x35, 0x19, 0x42, 0x01, 0x19, 0x20, 0x02, 0x15, + 0x10, 0x19, 0x14, 0x02, 0x06, 0x0c, 0x11, 0x0c, 0x01, 0x00, 0x0c, 0x01, + 0x00, 0x00, 0x01, 0x05, 0x00, 0x02, 0x21, 0x06, 0x1a, 0x00, 0x02, 0x28, + 0x1f, 0x28, 0x0c, 0x01, 0x28, 0x34, 0x02, 0x2d, 0x45, 0x28, 0x41, 0x02, + 0x3d, 0x4a, 0x31, 0x4a, 0x01, 0x43, 0x4a, 0x01, 0x43, 0x56, 0x01, 0x3d, + 0x56, 0x02, 0x2d, 0x5b, 0x31, 0x56, 0x02, 0x28, 0x6d, 0x28, 0x60, 0x01, + 0x28, 0x81, 0x02, 0x21, 0x9a, 0x28, 0x94, 0x02, 0x05, 0xa0, 0x1a, 0xa0, + 0x01, 0x00, 0xa0, 0x01, 0x00, 0x94, 0x11, 0x6d, 0x00, 0x01, 0x6d, 0x0f, + 0x02, 0x5c, 0x19, 0x64, 0x16, 0x02, 0x4d, 0x1c, 0x55, 0x1c, 0x02, 0x37, + 0x17, 0x43, 0x1c, 0x02, 0x36, 0x17, 0x36, 0x17, 0x02, 0x34, 0x16, 0x35, + 0x16, 0x02, 0x1f, 0x11, 0x27, 0x11, 0x02, 0x10, 0x14, 0x17, 0x11, 0x02, + 0x00, 0x1e, 0x08, 0x17, 0x01, 0x00, 0x0f, 0x02, 0x10, 0x05, 0x09, 0x08, + 0x02, 0x21, 0x02, 0x19, 0x02, 0x02, 0x36, 0x08, 0x2a, 0x02, 0x02, 0x38, + 0x08, 0x37, 0x08, 0x02, 0x39, 0x08, 0x38, 0x08, 0x02, 0x4f, 0x0e, 0x47, + 0x0e, 0x02, 0x5d, 0x0a, 0x56, 0x0e, 0x02, 0x6d, 0x00, 0x64, 0x08, 0x03, + 0x11, 0x15, 0x01, 0x00, 0x15, 0x01, 0x00, 0x00, 0x01, 0x11, 0x00, 0x01, + 0x11, 0x15, 0x05, 0x11, 0x56, 0x01, 0x00, 0x56, 0x01, 0x00, 0x1f, 0x01, + 0x02, 0x00, 0x01, 0x0f, 0x00, 0x01, 0x11, 0x1f, 0x01, 0x11, 0x56, 0x04, + 0x1b, 0x48, 0x01, 0x1b, 0x00, 0x02, 0x07, 0x0b, 0x0e, 0x01, 0x02, 0x00, + 0x24, 0x00, 0x14, 0x02, 0x07, 0x3d, 0x00, 0x34, 0x02, 0x1b, 0x48, 0x0e, + 0x47, 0x14, 0x4b, 0x1e, 0x01, 0x4b, 0x2d, 0x02, 0x40, 0x27, 0x45, 0x29, + 0x02, 0x34, 0x25, 0x3a, 0x25, 0x01, 0x34, 0x6f, 0x02, 0x40, 0x6d, 0x3a, + 0x6e, 0x02, 0x4b, 0x67, 0x45, 0x6a, 0x01, 0x4b, 0x75, 0x02, 0x41, 0x79, + 0x46, 0x78, 0x02, 0x34, 0x7c, 0x3a, 0x7b, 0x01, 0x34, 0x94, 0x01, 0x2c, + 0x94, 0x01, 0x2c, 0x7b, 0x02, 0x0c, 0x6d, 0x17, 0x7a, 0x02, 0x00, 0x4a, + 0x00, 0x60, 0x02, 0x0c, 0x27, 0x00, 0x34, 0x02, 0x2c, 0x18, 0x17, 0x1a, + 0x01, 0x2c, 0x00, 0x01, 0x34, 0x00, 0x01, 0x34, 0x18, 0x02, 0x40, 0x1a, + 0x3a, 0x18, 0x02, 0x4b, 0x1e, 0x46, 0x1b, 0x17, 0x52, 0x05, 0x01, 0x52, + 0x15, 0x02, 0x46, 0x0f, 0x4c, 0x11, 0x02, 0x3b, 0x0d, 0x40, 0x0d, 0x02, + 0x2a, 0x14, 0x2f, 0x0d, 0x02, 0x25, 0x2c, 0x25, 0x1b, 0x01, 0x25, 0x3f, + 0x01, 0x46, 0x3f, 0x01, 0x46, 0x4b, 0x01, 0x25, 0x4b, 0x01, 0x25, 0x72, + 0x01, 0x54, 0x72, 0x01, 0x54, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x72, + 0x01, 0x14, 0x72, 0x01, 0x14, 0x4b, 0x01, 0x03, 0x4b, 0x01, 0x03, 0x3f, + 0x01, 0x14, 0x3f, 0x01, 0x14, 0x2a, 0x02, 0x1d, 0x0a, 0x14, 0x15, 0x02, + 0x39, 0x00, 0x26, 0x00, 0x02, 0x45, 0x01, 0x3f, 0x00, 0x02, 0x52, 0x05, + 0x4c, 0x02, 0x1b, 0x43, 0x11, 0x01, 0x55, 0x00, 0x01, 0x5e, 0x0a, 0x01, + 0x4d, 0x1b, 0x02, 0x52, 0x25, 0x50, 0x20, 0x02, 0x53, 0x30, 0x53, 0x2a, + 0x02, 0x51, 0x3a, 0x53, 0x35, 0x02, 0x4d, 0x44, 0x50, 0x3f, 0x01, 0x5e, + 0x55, 0x01, 0x55, 0x5f, 0x01, 0x43, 0x4d, 0x02, 0x39, 0x52, 0x3e, 0x50, + 0x02, 0x2f, 0x54, 0x34, 0x54, 0x02, 0x25, 0x52, 0x2a, 0x54, 0x02, 0x1b, + 0x4e, 0x20, 0x50, 0x01, 0x0a, 0x5f, 0x01, 0x00, 0x55, 0x01, 0x12, 0x44, + 0x02, 0x0d, 0x39, 0x0e, 0x3f, 0x02, 0x0b, 0x30, 0x0b, 0x35, 0x02, 0x0d, + 0x25, 0x0b, 0x2a, 0x02, 0x12, 0x1b, 0x0e, 0x20, 0x01, 0x00, 0x0a, 0x01, + 0x0a, 0x00, 0x01, 0x1b, 0x12, 0x02, 0x25, 0x0d, 0x20, 0x0f, 0x02, 0x2f, + 0x0b, 0x2a, 0x0b, 0x02, 0x39, 0x0d, 0x34, 0x0b, 0x02, 0x43, 0x11, 0x3e, + 0x0f, 0x07, 0x2e, 0x16, 0x02, 0x27, 0x07, 0x2e, 0x0d, 0x02, 0x17, 0x00, + 0x21, 0x00, 0x02, 0x07, 0x07, 0x0e, 0x00, 0x02, 0x00, 0x16, 0x00, 0x0d, + 0x02, 0x07, 0x26, 0x00, 0x20, 0x02, 0x17, 0x2d, 0x0d, 0x2d, 0x02, 0x27, + 0x26, 0x21, 0x2d, 0x02, 0x2e, 0x16, 0x2e, 0x20, 0x18, 0x5c, 0x58, 0x01, + 0x39, 0x58, 0x01, 0x39, 0x7e, 0x01, 0x28, 0x7e, 0x01, 0x28, 0x58, 0x01, + 0x04, 0x58, 0x01, 0x04, 0x4e, 0x01, 0x28, 0x4e, 0x01, 0x28, 0x4a, 0x01, + 0x20, 0x3c, 0x01, 0x04, 0x3c, 0x01, 0x04, 0x32, 0x01, 0x1b, 0x32, 0x01, + 0x00, 0x00, 0x01, 0x10, 0x00, 0x01, 0x30, 0x3b, 0x01, 0x50, 0x00, 0x01, + 0x60, 0x00, 0x01, 0x46, 0x32, 0x01, 0x5c, 0x32, 0x01, 0x5c, 0x3c, 0x01, + 0x3f, 0x3c, 0x01, 0x39, 0x4a, 0x01, 0x39, 0x4e, 0x01, 0x5c, 0x4e, 0x01, + 0x5c, 0x58, 0x03, 0x0e, 0x00, 0x01, 0x0e, 0x40, 0x01, 0x00, 0x40, 0x01, + 0x00, 0x00, 0x01, 0x0e, 0x00, 0x07, 0x0a, 0x00, 0x02, 0x02, 0x08, 0x04, + 0x04, 0x02, 0x00, 0x10, 0x00, 0x0c, 0x02, 0x05, 0x1b, 0x00, 0x16, 0x02, + 0x20, 0x2c, 0x0b, 0x21, 0x02, 0x28, 0x25, 0x26, 0x29, 0x02, 0x2b, 0x1d, + 0x2b, 0x21, 0x02, 0x25, 0x11, 0x2b, 0x16, 0x02, 0x0a, 0x00, 0x1f, 0x0c, + 0x21, 0x3f, 0x04, 0x01, 0x3f, 0x13, 0x02, 0x32, 0x0e, 0x38, 0x0f, 0x02, + 0x27, 0x0c, 0x2c, 0x0c, 0x02, 0x1a, 0x0f, 0x1f, 0x0c, 0x02, 0x16, 0x19, + 0x16, 0x13, 0x02, 0x27, 0x2a, 0x16, 0x20, 0x02, 0x2a, 0x2c, 0x2a, 0x2b, + 0x02, 0x42, 0x3d, 0x3d, 0x36, 0x02, 0x48, 0x4c, 0x48, 0x43, 0x02, 0x43, + 0x59, 0x48, 0x53, 0x02, 0x38, 0x63, 0x40, 0x5f, 0x02, 0x3f, 0x6c, 0x3d, + 0x68, 0x02, 0x42, 0x76, 0x42, 0x71, 0x02, 0x39, 0x89, 0x42, 0x82, 0x02, + 0x22, 0x91, 0x30, 0x91, 0x02, 0x15, 0x8f, 0x1c, 0x91, 0x02, 0x07, 0x8c, + 0x0f, 0x8f, 0x01, 0x07, 0x7e, 0x02, 0x15, 0x83, 0x0f, 0x81, 0x02, 0x20, + 0x84, 0x1b, 0x84, 0x02, 0x2e, 0x81, 0x29, 0x84, 0x02, 0x33, 0x77, 0x33, + 0x7e, 0x02, 0x20, 0x64, 0x33, 0x6e, 0x01, 0x1d, 0x63, 0x02, 0x06, 0x52, + 0x0b, 0x58, 0x02, 0x00, 0x43, 0x00, 0x4c, 0x02, 0x04, 0x35, 0x00, 0x3b, + 0x02, 0x10, 0x2b, 0x08, 0x2f, 0x02, 0x08, 0x23, 0x0b, 0x28, 0x02, 0x06, + 0x18, 0x06, 0x1e, 0x02, 0x0f, 0x06, 0x06, 0x0d, 0x02, 0x25, 0x00, 0x17, + 0x00, 0x02, 0x32, 0x01, 0x2c, 0x00, 0x02, 0x3f, 0x04, 0x39, 0x02, 0x03, + 0x00, 0x00, 0x01, 0x11, 0x00, 0x01, 0x11, 0x11, 0x01, 0x00, 0x11, 0x01, + 0x00, 0x00, 0x0f, 0x3f, 0x00, 0x02, 0x56, 0x05, 0x4c, 0x00, 0x02, 0x6b, + 0x13, 0x62, 0x0a, 0x02, 0x79, 0x28, 0x74, 0x1c, 0x02, 0x7e, 0x3f, 0x7e, + 0x32, 0x02, 0x79, 0x58, 0x7e, 0x4c, 0x02, 0x6b, 0x6c, 0x74, 0x63, 0x02, + 0x56, 0x7a, 0x62, 0x75, 0x02, 0x3f, 0x7e, 0x4c, 0x7e, 0x02, 0x26, 0x7a, + 0x32, 0x7e, 0x02, 0x12, 0x6c, 0x1b, 0x75, 0x02, 0x04, 0x58, 0x09, 0x63, + 0x02, 0x00, 0x3f, 0x00, 0x4c, 0x02, 0x04, 0x28, 0x00, 0x32, 0x02, 0x12, + 0x13, 0x09, 0x1c, 0x02, 0x26, 0x05, 0x1b, 0x0a, 0x02, 0x3f, 0x00, 0x32, + 0x00, 0x0f, 0x37, 0x00, 0x02, 0x22, 0x04, 0x2c, 0x00, 0x02, 0x11, 0x10, + 0x18, 0x08, 0x02, 0x04, 0x22, 0x09, 0x18, 0x02, 0x00, 0x36, 0x00, 0x2b, + 0x02, 0x04, 0x4b, 0x00, 0x41, 0x02, 0x11, 0x5c, 0x09, 0x55, 0x02, 0x22, + 0x69, 0x18, 0x64, 0x02, 0x37, 0x6d, 0x2c, 0x6d, 0x02, 0x4c, 0x69, 0x42, + 0x6d, 0x02, 0x5d, 0x5c, 0x55, 0x64, 0x02, 0x69, 0x4b, 0x65, 0x55, 0x02, + 0x6d, 0x36, 0x6d, 0x42, 0x02, 0x69, 0x21, 0x6d, 0x2b, 0x02, 0x5d, 0x10, + 0x65, 0x18, 0x02, 0x4c, 0x04, 0x55, 0x08, 0x02, 0x37, 0x00, 0x42, 0x00, + 0x11, 0x3f, 0x05, 0x01, 0x3f, 0x10, 0x02, 0x34, 0x0c, 0x39, 0x0d, 0x02, + 0x29, 0x0b, 0x2f, 0x0b, 0x02, 0x15, 0x12, 0x1c, 0x0b, 0x02, 0x0e, 0x26, + 0x0e, 0x19, 0x02, 0x15, 0x3b, 0x0e, 0x34, 0x02, 0x2a, 0x43, 0x1c, 0x43, + 0x02, 0x34, 0x41, 0x2f, 0x43, 0x02, 0x3f, 0x3d, 0x39, 0x40, 0x01, 0x3f, + 0x48, 0x02, 0x33, 0x4c, 0x39, 0x4b, 0x02, 0x28, 0x4d, 0x2e, 0x4d, 0x02, + 0x0b, 0x43, 0x15, 0x4d, 0x02, 0x00, 0x26, 0x00, 0x38, 0x02, 0x0b, 0x0b, + 0x00, 0x15, 0x02, 0x28, 0x00, 0x15, 0x00, 0x02, 0x34, 0x02, 0x2e, 0x00, + 0x02, 0x3f, 0x05, 0x39, 0x03, 0x03, 0x00, 0x00, 0x01, 0x3b, 0x00, 0x01, + 0x3b, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x13, 0x3d, 0x1e, 0x01, + 0x3d, 0x44, 0x01, 0x30, 0x44, 0x01, 0x30, 0x3b, 0x02, 0x26, 0x43, 0x2c, + 0x3f, 0x02, 0x18, 0x45, 0x20, 0x45, 0x02, 0x07, 0x40, 0x0d, 0x45, 0x02, + 0x00, 0x30, 0x00, 0x3a, 0x02, 0x09, 0x1f, 0x00, 0x25, 0x02, 0x21, 0x19, + 0x11, 0x19, 0x01, 0x30, 0x19, 0x01, 0x30, 0x19, 0x02, 0x2b, 0x0d, 0x30, + 0x11, 0x02, 0x1c, 0x09, 0x26, 0x09, 0x02, 0x11, 0x0a, 0x17, 0x09, 0x02, + 0x06, 0x0f, 0x0b, 0x0c, 0x01, 0x06, 0x04, 0x02, 0x12, 0x01, 0x0c, 0x02, + 0x02, 0x1e, 0x00, 0x18, 0x00, 0x02, 0x35, 0x07, 0x2e, 0x00, 0x02, 0x3d, + 0x1e, 0x3d, 0x0e, 0x07, 0x19, 0x00, 0x02, 0x06, 0x03, 0x0a, 0x00, 0x02, + 0x00, 0x0c, 0x00, 0x05, 0x02, 0x04, 0x15, 0x00, 0x11, 0x02, 0x0f, 0x18, + 0x08, 0x18, 0x02, 0x1d, 0x12, 0x18, 0x18, 0x02, 0x23, 0x03, 0x23, 0x0c, + 0x01, 0x23, 0x00, 0x01, 0x19, 0x00, 0x06, 0x28, 0x00, 0x01, 0x28, 0x10, + 0x01, 0x0e, 0x27, 0x01, 0x28, 0x3e, 0x01, 0x28, 0x4e, 0x01, 0x00, 0x2b, + 0x01, 0x00, 0x23, 0x01, 0x28, 0x00, 0x06, 0x28, 0x00, 0x01, 0x28, 0x10, + 0x01, 0x0f, 0x27, 0x01, 0x28, 0x3e, 0x01, 0x28, 0x4e, 0x01, 0x00, 0x2b, + 0x01, 0x00, 0x23, 0x01, 0x28, 0x00, 0x05, 0x00, 0x00, 0x01, 0x6d, 0x00, + 0x01, 0x6d, 0x31, 0x01, 0x5e, 0x31, 0x01, 0x5e, 0x0e, 0x01, 0x00, 0x0e, + 0x01, 0x00, 0x00, 0x06, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x17, + 0x01, 0x0b, 0x17, 0x02, 0x18, 0x14, 0x13, 0x17, 0x02, 0x1b, 0x0b, 0x1b, + 0x11, 0x02, 0x18, 0x02, 0x1b, 0x05, 0x02, 0x0b, 0x00, 0x13, 0x00, 0x0f, + 0x18, 0x00, 0x02, 0x2e, 0x04, 0x27, 0x00, 0x02, 0x35, 0x14, 0x35, 0x0a, + 0x02, 0x31, 0x20, 0x35, 0x1c, 0x02, 0x25, 0x26, 0x2d, 0x25, 0x02, 0x29, + 0x2a, 0x27, 0x27, 0x02, 0x30, 0x33, 0x2c, 0x2d, 0x01, 0x3c, 0x46, 0x01, + 0x2d, 0x46, 0x01, 0x23, 0x35, 0x02, 0x1a, 0x2a, 0x1d, 0x2c, 0x02, 0x12, + 0x28, 0x17, 0x28, 0x01, 0x0d, 0x28, 0x01, 0x0d, 0x46, 0x01, 0x00, 0x46, + 0x01, 0x00, 0x00, 0x01, 0x18, 0x00, 0x03, 0x00, 0x00, 0x01, 0x32, 0x00, + 0x01, 0x32, 0x0d, 0x01, 0x00, 0x0d, 0x01, 0x00, 0x00, 0x07, 0x10, 0x00, + 0x02, 0x05, 0x05, 0x09, 0x00, 0x02, 0x00, 0x11, 0x00, 0x0a, 0x02, 0x05, + 0x1c, 0x00, 0x17, 0x02, 0x10, 0x20, 0x09, 0x20, 0x02, 0x1c, 0x1c, 0x17, + 0x20, 0x02, 0x21, 0x11, 0x21, 0x17, 0x02, 0x1c, 0x05, 0x21, 0x0a, 0x02, + 0x10, 0x00, 0x17, 0x00, 0x09, 0x1b, 0x00, 0x02, 0x25, 0x02, 0x21, 0x00, + 0x02, 0x2e, 0x08, 0x2b, 0x04, 0x02, 0x34, 0x11, 0x32, 0x0b, 0x02, 0x36, + 0x1b, 0x36, 0x15, 0x02, 0x2e, 0x2e, 0x36, 0x26, 0x02, 0x1b, 0x35, 0x26, + 0x35, 0x02, 0x08, 0x2e, 0x10, 0x35, 0x02, 0x00, 0x1b, 0x00, 0x26, 0x02, + 0x08, 0x08, 0x00, 0x0f, 0x02, 0x1b, 0x00, 0x10, 0x00, 0x0b, 0x3e, 0x00, + 0x01, 0x3e, 0x20, 0x01, 0x6d, 0x20, 0x01, 0x6d, 0x2f, 0x01, 0x3e, 0x2f, + 0x01, 0x3e, 0x4f, 0x01, 0x30, 0x4f, 0x01, 0x30, 0x2f, 0x01, 0x00, 0x2f, + 0x01, 0x00, 0x20, 0x01, 0x30, 0x20, 0x01, 0x30, 0x00, 0x01, 0x3e, 0x00, + 0x10, 0x0e, 0x3f, 0x01, 0x32, 0x3f, 0x01, 0x32, 0x48, 0x01, 0x00, 0x48, + 0x01, 0x00, 0x3f, 0x02, 0x08, 0x38, 0x03, 0x3d, 0x02, 0x25, 0x15, 0x25, + 0x1d, 0x02, 0x21, 0x0c, 0x25, 0x10, 0x02, 0x15, 0x09, 0x1c, 0x09, 0x02, + 0x0c, 0x0a, 0x11, 0x09, 0x02, 0x00, 0x0e, 0x06, 0x0c, 0x01, 0x00, 0x04, + 0x02, 0x0c, 0x01, 0x07, 0x02, 0x02, 0x17, 0x00, 0x12, 0x00, 0x02, 0x2b, + 0x05, 0x23, 0x00, 0x02, 0x32, 0x14, 0x32, 0x0b, 0x02, 0x16, 0x39, 0x32, + 0x20, 0x02, 0x0e, 0x3f, 0x11, 0x3d, 0x1c, 0x24, 0x21, 0x02, 0x30, 0x27, + 0x2c, 0x23, 0x02, 0x35, 0x33, 0x35, 0x2c, 0x02, 0x2d, 0x43, 0x35, 0x3d, + 0x02, 0x16, 0x49, 0x25, 0x49, 0x02, 0x0c, 0x48, 0x11, 0x49, 0x02, 0x00, + 0x45, 0x06, 0x47, 0x01, 0x00, 0x3b, 0x02, 0x0a, 0x3f, 0x05, 0x3d, 0x02, + 0x14, 0x3f, 0x0f, 0x3f, 0x02, 0x23, 0x3c, 0x1e, 0x3f, 0x02, 0x28, 0x33, + 0x28, 0x39, 0x02, 0x23, 0x2a, 0x28, 0x2c, 0x02, 0x15, 0x26, 0x1f, 0x26, + 0x01, 0x0e, 0x26, 0x01, 0x0e, 0x1d, 0x01, 0x16, 0x1d, 0x02, 0x22, 0x1a, + 0x1e, 0x1d, 0x02, 0x26, 0x13, 0x26, 0x17, 0x02, 0x22, 0x0c, 0x26, 0x0e, + 0x02, 0x16, 0x09, 0x1e, 0x09, 0x02, 0x0e, 0x0a, 0x12, 0x09, 0x02, 0x03, + 0x0c, 0x0a, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x0e, 0x00, 0x09, 0x01, 0x02, + 0x18, 0x00, 0x14, 0x00, 0x02, 0x2b, 0x04, 0x25, 0x00, 0x02, 0x33, 0x12, + 0x33, 0x0a, 0x02, 0x2f, 0x1b, 0x33, 0x17, 0x02, 0x24, 0x21, 0x2b, 0x1f, + 0x03, 0x18, 0x00, 0x01, 0x29, 0x00, 0x01, 0x0d, 0x20, 0x01, 0x00, 0x20, + 0x01, 0x18, 0x00, 0x18, 0x00, 0x83, 0x01, 0x00, 0x00, 0x01, 0x0f, 0x00, + 0x01, 0x0f, 0x3b, 0x02, 0x15, 0x4e, 0x0f, 0x48, 0x02, 0x26, 0x55, 0x1b, + 0x55, 0x02, 0x39, 0x4e, 0x33, 0x55, 0x02, 0x40, 0x38, 0x40, 0x46, 0x01, + 0x40, 0x00, 0x01, 0x4f, 0x00, 0x01, 0x4f, 0x4a, 0x02, 0x51, 0x51, 0x4f, + 0x4f, 0x02, 0x55, 0x54, 0x52, 0x54, 0x02, 0x57, 0x53, 0x56, 0x54, 0x02, + 0x5b, 0x52, 0x59, 0x53, 0x01, 0x5b, 0x5e, 0x02, 0x55, 0x61, 0x57, 0x60, + 0x02, 0x4e, 0x62, 0x51, 0x62, 0x02, 0x45, 0x5f, 0x48, 0x62, 0x02, 0x40, + 0x55, 0x42, 0x5b, 0x02, 0x36, 0x5f, 0x3c, 0x5b, 0x02, 0x28, 0x62, 0x30, + 0x62, 0x02, 0x19, 0x5f, 0x1f, 0x62, 0x02, 0x0f, 0x55, 0x13, 0x5b, 0x01, + 0x0f, 0x83, 0x01, 0x00, 0x83, 0x0b, 0x28, 0x00, 0x01, 0x4e, 0x00, 0x01, + 0x4e, 0x8f, 0x01, 0x42, 0x8f, 0x01, 0x42, 0x0a, 0x01, 0x33, 0x0a, 0x01, + 0x33, 0x8f, 0x01, 0x26, 0x8f, 0x01, 0x26, 0x47, 0x02, 0x0a, 0x3d, 0x14, + 0x46, 0x02, 0x00, 0x24, 0x00, 0x33, 0x02, 0x0b, 0x0a, 0x00, 0x13, 0x02, + 0x28, 0x00, 0x16, 0x00, 0x0d, 0x1a, 0x00, 0x02, 0x22, 0x0a, 0x1f, 0x06, + 0x02, 0x24, 0x13, 0x24, 0x0f, 0x02, 0x1f, 0x1e, 0x24, 0x1a, 0x02, 0x0f, + 0x22, 0x19, 0x22, 0x02, 0x08, 0x21, 0x0c, 0x22, 0x02, 0x00, 0x20, 0x04, + 0x21, 0x01, 0x00, 0x15, 0x02, 0x06, 0x17, 0x04, 0x16, 0x02, 0x0e, 0x17, + 0x0a, 0x17, 0x02, 0x15, 0x15, 0x13, 0x17, 0x02, 0x18, 0x0f, 0x18, 0x13, + 0x02, 0x16, 0x09, 0x18, 0x0d, 0x02, 0x11, 0x00, 0x14, 0x06, 0x01, 0x1a, + 0x00, 0x0a, 0x02, 0x3d, 0x01, 0x13, 0x3d, 0x01, 0x13, 0x09, 0x01, 0x00, + 0x0d, 0x01, 0x00, 0x03, 0x01, 0x14, 0x00, 0x01, 0x20, 0x00, 0x01, 0x20, + 0x3d, 0x01, 0x31, 0x3d, 0x01, 0x31, 0x46, 0x01, 0x02, 0x46, 0x01, 0x02, + 0x3d, 0x07, 0x21, 0x00, 0x02, 0x39, 0x09, 0x30, 0x00, 0x02, 0x41, 0x23, + 0x41, 0x13, 0x02, 0x39, 0x3c, 0x41, 0x33, 0x02, 0x21, 0x45, 0x30, 0x45, + 0x02, 0x09, 0x3c, 0x12, 0x45, 0x02, 0x00, 0x23, 0x00, 0x32, 0x02, 0x09, + 0x09, 0x00, 0x13, 0x02, 0x21, 0x00, 0x12, 0x00, 0x07, 0x14, 0x00, 0x02, + 0x05, 0x07, 0x0b, 0x00, 0x02, 0x00, 0x19, 0x00, 0x0e, 0x02, 0x05, 0x2b, + 0x00, 0x25, 0x02, 0x14, 0x32, 0x0b, 0x32, 0x02, 0x22, 0x2b, 0x1c, 0x32, + 0x02, 0x27, 0x19, 0x27, 0x25, 0x02, 0x22, 0x07, 0x27, 0x0e, 0x02, 0x14, + 0x00, 0x1d, 0x00, 0x06, 0x00, 0x00, 0x01, 0x28, 0x23, 0x01, 0x28, 0x2b, + 0x01, 0x00, 0x4e, 0x01, 0x00, 0x3e, 0x01, 0x1a, 0x27, 0x01, 0x00, 0x10, + 0x01, 0x00, 0x00, 0x03, 0x4f, 0x00, 0x01, 0x5c, 0x00, 0x01, 0x0d, 0x83, + 0x01, 0x00, 0x83, 0x01, 0x4f, 0x00, 0x02, 0x1b, 0x00, 0x01, 0x00, 0x24, + 0x01, 0x1b, 0x24, 0x01, 0x1b, 0x00, 0x0a, 0x21, 0x00, 0x01, 0x2f, 0x00, + 0x01, 0x2f, 0x2e, 0x01, 0x3b, 0x2e, 0x01, 0x3b, 0x37, 0x01, 0x2f, 0x37, + 0x01, 0x2f, 0x47, 0x01, 0x23, 0x47, 0x01, 0x23, 0x37, 0x01, 0x00, 0x37, + 0x01, 0x00, 0x2d, 0x01, 0x21, 0x00, 0x10, 0x0f, 0x3f, 0x01, 0x33, 0x3f, + 0x01, 0x33, 0x48, 0x01, 0x00, 0x48, 0x01, 0x00, 0x3f, 0x02, 0x08, 0x38, + 0x03, 0x3d, 0x02, 0x26, 0x16, 0x26, 0x1e, 0x02, 0x22, 0x0d, 0x26, 0x10, + 0x02, 0x16, 0x09, 0x1d, 0x09, 0x02, 0x0c, 0x0b, 0x11, 0x09, 0x02, 0x01, + 0x0f, 0x07, 0x0c, 0x01, 0x01, 0x05, 0x02, 0x0d, 0x01, 0x07, 0x03, 0x02, + 0x17, 0x00, 0x13, 0x00, 0x02, 0x2b, 0x05, 0x24, 0x00, 0x02, 0x33, 0x14, + 0x33, 0x0b, 0x02, 0x17, 0x39, 0x33, 0x20, 0x02, 0x0f, 0x3f, 0x12, 0x3d, + 0x17, 0x1e, 0x00, 0x01, 0x2e, 0x00, 0x01, 0x2e, 0x0e, 0x02, 0x2c, 0x1c, + 0x2e, 0x16, 0x02, 0x22, 0x29, 0x29, 0x22, 0x01, 0x1b, 0x31, 0x02, 0x13, + 0x39, 0x16, 0x35, 0x02, 0x11, 0x41, 0x11, 0x3d, 0x02, 0x16, 0x4d, 0x11, + 0x48, 0x02, 0x25, 0x52, 0x1d, 0x52, 0x02, 0x34, 0x4f, 0x2c, 0x52, 0x02, + 0x43, 0x46, 0x3b, 0x4c, 0x01, 0x43, 0x57, 0x02, 0x34, 0x5e, 0x3c, 0x5b, + 0x02, 0x23, 0x60, 0x2b, 0x60, 0x02, 0x0a, 0x58, 0x13, 0x60, 0x02, 0x00, + 0x42, 0x00, 0x50, 0x02, 0x03, 0x35, 0x00, 0x3c, 0x02, 0x0e, 0x29, 0x06, + 0x30, 0x01, 0x15, 0x21, 0x02, 0x1b, 0x1b, 0x19, 0x1d, 0x02, 0x1d, 0x16, + 0x1d, 0x19, 0x02, 0x1e, 0x12, 0x1e, 0x15, 0x02, 0x1e, 0x0b, 0x1e, 0x0f, + 0x01, 0x1e, 0x00, 0x03, 0x0f, 0x00, 0x01, 0x20, 0x16, 0x01, 0x13, 0x16, + 0x01, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x03, 0x11, 0x00, 0x01, 0x20, 0x00, + 0x01, 0x0d, 0x16, 0x01, 0x00, 0x16, 0x01, 0x11, 0x00, 0x06, 0x12, 0x00, + 0x01, 0x22, 0x00, 0x01, 0x34, 0x16, 0x01, 0x28, 0x16, 0x01, 0x1a, 0x07, + 0x01, 0x0c, 0x16, 0x01, 0x00, 0x16, 0x01, 0x12, 0x00, 0x15, 0x1c, 0x0f, + 0x01, 0x17, 0x0c, 0x02, 0x14, 0x0b, 0x15, 0x0b, 0x02, 0x11, 0x0a, 0x12, + 0x0a, 0x02, 0x0c, 0x0c, 0x0e, 0x0a, 0x02, 0x0b, 0x12, 0x0b, 0x0e, 0x01, + 0x0b, 0x13, 0x01, 0x00, 0x13, 0x02, 0x05, 0x05, 0x00, 0x0a, 0x02, 0x10, + 0x00, 0x09, 0x00, 0x02, 0x16, 0x00, 0x13, 0x00, 0x02, 0x1d, 0x04, 0x18, + 0x01, 0x01, 0x21, 0x06, 0x02, 0x25, 0x08, 0x23, 0x07, 0x02, 0x27, 0x08, + 0x26, 0x08, 0x02, 0x2c, 0x06, 0x2a, 0x08, 0x02, 0x2e, 0x00, 0x2e, 0x04, + 0x01, 0x2e, 0x00, 0x01, 0x38, 0x00, 0x02, 0x34, 0x0d, 0x38, 0x08, 0x02, + 0x28, 0x13, 0x30, 0x13, 0x02, 0x23, 0x12, 0x25, 0x13, 0x02, 0x1c, 0x0f, + 0x20, 0x11, 0x03, 0x00, 0x00, 0x01, 0x12, 0x00, 0x01, 0x12, 0x11, 0x01, + 0x00, 0x11, 0x01, 0x00, 0x00, 0x07, 0x19, 0x0d, 0x02, 0x15, 0x04, 0x19, + 0x07, 0x02, 0x0c, 0x00, 0x11, 0x00, 0x02, 0x03, 0x03, 0x06, 0x00, 0x02, + 0x00, 0x0d, 0x00, 0x07, 0x02, 0x03, 0x16, 0x00, 0x12, 0x02, 0x0c, 0x1a, + 0x07, 0x1a, 0x02, 0x15, 0x16, 0x11, 0x1a, 0x02, 0x19, 0x0d, 0x19, 0x12, + 0x0e, 0x2e, 0x2a, 0x02, 0x26, 0x22, 0x28, 0x27, 0x02, 0x23, 0x17, 0x23, + 0x1d, 0x02, 0x2a, 0x06, 0x23, 0x0d, 0x02, 0x3a, 0x00, 0x30, 0x00, 0x02, + 0x4b, 0x06, 0x44, 0x00, 0x02, 0x52, 0x17, 0x52, 0x0d, 0x02, 0x4f, 0x22, + 0x52, 0x1d, 0x02, 0x47, 0x2a, 0x4c, 0x27, 0x01, 0x74, 0xa0, 0x01, 0x63, + 0xa0, 0x01, 0x57, 0x80, 0x01, 0x1e, 0x80, 0x01, 0x13, 0xa0, 0x01, 0x00, + 0xa0, 0x01, 0x2e, 0x2a, 0x0f, 0x9c, 0x00, 0x01, 0x9c, 0x0e, 0x01, 0x5c, + 0x0e, 0x01, 0x5c, 0x34, 0x01, 0x99, 0x34, 0x01, 0x99, 0x42, 0x01, 0x5c, + 0x42, 0x01, 0x5c, 0x70, 0x01, 0x9d, 0x70, 0x01, 0x9d, 0x7e, 0x01, 0x4c, + 0x7e, 0x01, 0x4c, 0x5e, 0x01, 0x1f, 0x5e, 0x01, 0x11, 0x7e, 0x01, 0x00, + 0x7e, 0x01, 0x34, 0x00, 0x01, 0x9c, 0x00, 0x03, 0x1c, 0x00, 0x01, 0x00, + 0x43, 0x01, 0x28, 0x43, 0x01, 0x28, 0x00, 0x01, 0x1c, 0x00, 0x0d, 0x1a, + 0x00, 0x02, 0x21, 0x0a, 0x1f, 0x06, 0x02, 0x23, 0x13, 0x23, 0x0f, 0x02, + 0x1e, 0x1e, 0x23, 0x1a, 0x02, 0x0f, 0x22, 0x19, 0x22, 0x02, 0x08, 0x21, + 0x0b, 0x22, 0x02, 0x00, 0x20, 0x03, 0x21, 0x01, 0x00, 0x15, 0x02, 0x06, + 0x17, 0x03, 0x16, 0x02, 0x0d, 0x17, 0x09, 0x17, 0x02, 0x14, 0x15, 0x12, + 0x17, 0x02, 0x17, 0x0f, 0x17, 0x13, 0x02, 0x15, 0x09, 0x17, 0x0d, 0x02, + 0x10, 0x00, 0x14, 0x06, 0x01, 0x1a, 0x00, 0x03, 0x10, 0x00, 0x01, 0x20, + 0x16, 0x01, 0x13, 0x16, 0x01, 0x00, 0x00, 0x01, 0x10, 0x00, 0x06, 0x12, + 0x00, 0x01, 0x21, 0x00, 0x01, 0x34, 0x16, 0x01, 0x27, 0x16, 0x01, 0x1a, + 0x07, 0x01, 0x0c, 0x16, 0x01, 0x00, 0x16, 0x01, 0x12, 0x00, 0x03, 0x11, + 0x00, 0x01, 0x21, 0x00, 0x01, 0x0d, 0x16, 0x01, 0x00, 0x16, 0x01, 0x11, + 0x00, 0x06, 0x12, 0x00, 0x01, 0x22, 0x00, 0x01, 0x33, 0x16, 0x01, 0x28, + 0x16, 0x01, 0x1a, 0x07, 0x01, 0x0b, 0x16, 0x01, 0x00, 0x16, 0x01, 0x12, + 0x00, 0x0a, 0x11, 0x00, 0x01, 0x34, 0x00, 0x02, 0x6a, 0x0f, 0x58, 0x00, + 0x02, 0x7b, 0x3f, 0x7b, 0x1e, 0x02, 0x6a, 0x6f, 0x7b, 0x60, 0x02, 0x34, + 0x7e, 0x58, 0x7e, 0x01, 0x11, 0x7e, 0x01, 0x11, 0x43, 0x01, 0x00, 0x43, + 0x01, 0x00, 0x37, 0x01, 0x11, 0x37, 0x01, 0x11, 0x00, 0x0a, 0x00, 0x00, + 0x01, 0x00, 0x29, 0x01, 0x1d, 0x29, 0x01, 0x1d, 0x35, 0x01, 0x00, 0x35, + 0x01, 0x00, 0x62, 0x01, 0x14, 0x62, 0x02, 0x3b, 0x57, 0x2f, 0x62, 0x02, + 0x47, 0x31, 0x47, 0x4a, 0x02, 0x3b, 0x0b, 0x47, 0x18, 0x02, 0x14, 0x00, + 0x2f, 0x00, 0x01, 0x00, 0x00, 0x15, 0x1c, 0x0f, 0x01, 0x18, 0x0c, 0x02, + 0x14, 0x0b, 0x16, 0x0b, 0x02, 0x12, 0x0a, 0x13, 0x0a, 0x02, 0x0d, 0x0c, + 0x0f, 0x0a, 0x02, 0x0b, 0x12, 0x0b, 0x0e, 0x01, 0x0b, 0x13, 0x01, 0x00, + 0x13, 0x02, 0x05, 0x05, 0x00, 0x0a, 0x02, 0x11, 0x00, 0x09, 0x00, 0x02, + 0x16, 0x00, 0x14, 0x00, 0x02, 0x1d, 0x04, 0x19, 0x01, 0x01, 0x22, 0x06, + 0x02, 0x25, 0x08, 0x24, 0x07, 0x02, 0x28, 0x08, 0x26, 0x08, 0x02, 0x2d, + 0x06, 0x2b, 0x08, 0x02, 0x2e, 0x00, 0x2e, 0x04, 0x01, 0x2e, 0x00, 0x01, + 0x39, 0x00, 0x02, 0x34, 0x0d, 0x39, 0x08, 0x02, 0x29, 0x13, 0x30, 0x13, + 0x02, 0x23, 0x12, 0x26, 0x13, 0x02, 0x1c, 0x0f, 0x21, 0x11, 0x03, 0x11, + 0x00, 0x01, 0x21, 0x16, 0x01, 0x14, 0x16, 0x01, 0x00, 0x00, 0x01, 0x11, + 0x00, 0x06, 0x11, 0x00, 0x01, 0x22, 0x00, 0x01, 0x33, 0x16, 0x01, 0x28, + 0x16, 0x01, 0x19, 0x07, 0x01, 0x0b, 0x16, 0x01, 0x00, 0x16, 0x01, 0x11, + 0x00, 0x15, 0x1b, 0x0f, 0x01, 0x16, 0x0c, 0x02, 0x13, 0x0b, 0x14, 0x0b, + 0x02, 0x11, 0x0a, 0x11, 0x0a, 0x02, 0x0c, 0x0c, 0x0d, 0x0a, 0x02, 0x0a, + 0x12, 0x0a, 0x0e, 0x01, 0x0a, 0x13, 0x01, 0x00, 0x13, 0x02, 0x04, 0x05, + 0x00, 0x0a, 0x02, 0x0f, 0x00, 0x08, 0x00, 0x02, 0x15, 0x00, 0x13, 0x00, + 0x02, 0x1b, 0x04, 0x17, 0x01, 0x01, 0x20, 0x06, 0x02, 0x24, 0x08, 0x22, + 0x07, 0x02, 0x26, 0x08, 0x26, 0x08, 0x02, 0x2b, 0x06, 0x29, 0x08, 0x02, + 0x2c, 0x00, 0x2c, 0x04, 0x01, 0x2c, 0x00, 0x01, 0x37, 0x00, 0x02, 0x32, + 0x0d, 0x37, 0x08, 0x02, 0x27, 0x13, 0x2e, 0x13, 0x02, 0x21, 0x12, 0x24, + 0x13, 0x02, 0x1b, 0x0f, 0x1f, 0x11, 0x03, 0x00, 0x00, 0x01, 0x10, 0x00, + 0x01, 0x10, 0x11, 0x01, 0x00, 0x11, 0x01, 0x00, 0x00, 0x0b, 0x61, 0x0a, + 0x01, 0x3b, 0x31, 0x01, 0x61, 0x58, 0x01, 0x58, 0x62, 0x01, 0x30, 0x3b, + 0x01, 0x0a, 0x62, 0x01, 0x00, 0x58, 0x01, 0x26, 0x31, 0x01, 0x00, 0x0a, + 0x01, 0x0a, 0x00, 0x01, 0x30, 0x26, 0x01, 0x58, 0x00, 0x01, 0x61, 0x0a, + 0x06, 0x3d, 0x00, 0x01, 0x00, 0x48, 0x02, 0x0c, 0x50, 0x06, 0x4d, 0x02, + 0x1b, 0x53, 0x13, 0x53, 0x02, 0x39, 0x45, 0x2e, 0x53, 0x02, 0x43, 0x1f, + 0x43, 0x37, 0x02, 0x42, 0x0e, 0x43, 0x16, 0x02, 0x3d, 0x00, 0x41, 0x06, + 0x06, 0x43, 0x0b, 0x02, 0x37, 0x03, 0x3e, 0x05, 0x02, 0x28, 0x00, 0x31, + 0x00, 0x02, 0x0b, 0x0d, 0x15, 0x00, 0x02, 0x00, 0x33, 0x00, 0x1c, 0x02, + 0x01, 0x45, 0x00, 0x3d, 0x02, 0x06, 0x53, 0x03, 0x4c, 0x01, 0x43, 0x0b, + 0x11, 0x0e, 0x72, 0x02, 0x04, 0x5e, 0x07, 0x69, 0x02, 0x00, 0x45, 0x00, + 0x53, 0x02, 0x11, 0x15, 0x00, 0x27, 0x02, 0x3b, 0x04, 0x20, 0x04, 0x02, + 0x50, 0x07, 0x46, 0x04, 0x02, 0x61, 0x10, 0x59, 0x0a, 0x01, 0x6e, 0x00, + 0x01, 0x77, 0x08, 0x01, 0x68, 0x19, 0x02, 0x72, 0x2d, 0x6f, 0x21, 0x02, + 0x76, 0x45, 0x76, 0x38, 0x02, 0x66, 0x75, 0x76, 0x63, 0x02, 0x3b, 0x87, + 0x56, 0x87, 0x02, 0x27, 0x84, 0x31, 0x87, 0x02, 0x16, 0x7a, 0x1e, 0x80, + 0x01, 0x09, 0x8b, 0x01, 0x00, 0x83, 0x01, 0x0e, 0x72, 0x06, 0x11, 0x00, + 0x01, 0x21, 0x00, 0x01, 0x33, 0x16, 0x01, 0x27, 0x16, 0x01, 0x1a, 0x07, + 0x01, 0x0b, 0x16, 0x01, 0x00, 0x16, 0x01, 0x11, 0x00, 0x0a, 0x00, 0x00, + 0x01, 0x11, 0x00, 0x01, 0x11, 0x16, 0x01, 0x26, 0x16, 0x02, 0x46, 0x1f, + 0x3c, 0x16, 0x02, 0x52, 0x3b, 0x52, 0x29, 0x02, 0x46, 0x58, 0x52, 0x4e, + 0x02, 0x26, 0x61, 0x3c, 0x61, 0x01, 0x11, 0x61, 0x01, 0x11, 0x7e, 0x01, + 0x00, 0x7e, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x2f, 0x01, + 0x15, 0x2f, 0x02, 0x28, 0x29, 0x22, 0x2f, 0x02, 0x2f, 0x17, 0x2f, 0x23, + 0x02, 0x28, 0x06, 0x2f, 0x0c, 0x02, 0x15, 0x00, 0x22, 0x00, 0x01, 0x00, + 0x00, 0x21, 0x00, 0x24, 0x02, 0x0b, 0x0a, 0x00, 0x13, 0x02, 0x27, 0x00, + 0x15, 0x00, 0x02, 0x42, 0x0a, 0x39, 0x00, 0x02, 0x4c, 0x27, 0x4c, 0x14, + 0x02, 0x38, 0x2d, 0x3f, 0x27, 0x02, 0x31, 0x3a, 0x31, 0x32, 0x02, 0x33, + 0x41, 0x31, 0x3e, 0x02, 0x3b, 0x48, 0x35, 0x45, 0x01, 0x40, 0x4b, 0x02, + 0x52, 0x5a, 0x4e, 0x54, 0x02, 0x56, 0x69, 0x56, 0x60, 0x02, 0x4c, 0x80, + 0x56, 0x78, 0x02, 0x31, 0x87, 0x43, 0x87, 0x02, 0x26, 0x86, 0x2c, 0x87, + 0x02, 0x1a, 0x84, 0x20, 0x85, 0x01, 0x1a, 0x75, 0x02, 0x27, 0x79, 0x21, + 0x78, 0x02, 0x32, 0x7a, 0x2d, 0x7a, 0x02, 0x41, 0x76, 0x3c, 0x7a, 0x02, + 0x46, 0x6b, 0x46, 0x72, 0x02, 0x44, 0x62, 0x46, 0x65, 0x02, 0x39, 0x59, + 0x42, 0x5e, 0x01, 0x31, 0x54, 0x02, 0x25, 0x49, 0x28, 0x4f, 0x02, 0x22, + 0x3c, 0x22, 0x43, 0x02, 0x28, 0x2a, 0x22, 0x31, 0x02, 0x3c, 0x1f, 0x2f, + 0x22, 0x02, 0x36, 0x12, 0x3b, 0x17, 0x02, 0x27, 0x0d, 0x31, 0x0d, 0x02, + 0x16, 0x13, 0x1c, 0x0d, 0x02, 0x11, 0x24, 0x11, 0x19, 0x01, 0x11, 0x84, + 0x01, 0x00, 0x84, 0x01, 0x00, 0x24, 0x03, 0x17, 0x00, 0x01, 0x28, 0x00, + 0x01, 0x0c, 0x20, 0x01, 0x00, 0x20, 0x01, 0x17, 0x00, 0x06, 0x15, 0x00, + 0x01, 0x21, 0x00, 0x01, 0x36, 0x20, 0x01, 0x2b, 0x20, 0x01, 0x1b, 0x0b, + 0x01, 0x0c, 0x20, 0x01, 0x00, 0x20, 0x01, 0x15, 0x00, 0x13, 0x1c, 0x13, + 0x01, 0x17, 0x0e, 0x02, 0x14, 0x0c, 0x15, 0x0d, 0x02, 0x11, 0x0b, 0x13, + 0x0b, 0x02, 0x0c, 0x0e, 0x0e, 0x0b, 0x02, 0x0b, 0x18, 0x0b, 0x11, 0x01, + 0x00, 0x18, 0x02, 0x05, 0x07, 0x00, 0x0d, 0x02, 0x11, 0x00, 0x09, 0x00, + 0x02, 0x17, 0x02, 0x14, 0x00, 0x02, 0x1d, 0x06, 0x19, 0x03, 0x01, 0x21, + 0x0b, 0x02, 0x25, 0x0d, 0x24, 0x0c, 0x02, 0x27, 0x0d, 0x26, 0x0d, 0x02, + 0x2c, 0x0b, 0x2a, 0x0d, 0x02, 0x2e, 0x00, 0x2e, 0x07, 0x01, 0x38, 0x00, + 0x02, 0x34, 0x12, 0x38, 0x0c, 0x02, 0x28, 0x18, 0x2f, 0x18, 0x02, 0x21, + 0x17, 0x24, 0x18, 0x02, 0x1c, 0x13, 0x1f, 0x15, 0x07, 0x1a, 0x0d, 0x02, + 0x17, 0x04, 0x1a, 0x07, 0x02, 0x0d, 0x00, 0x12, 0x00, 0x02, 0x04, 0x04, + 0x08, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x07, 0x02, 0x04, 0x16, 0x00, 0x13, + 0x02, 0x0d, 0x1a, 0x08, 0x1a, 0x02, 0x17, 0x16, 0x12, 0x1a, 0x02, 0x1a, + 0x0d, 0x1a, 0x13, 0x07, 0x2e, 0x17, 0x02, 0x27, 0x28, 0x2e, 0x21, 0x02, + 0x17, 0x2f, 0x21, 0x2f, 0x02, 0x07, 0x28, 0x0e, 0x2f, 0x02, 0x00, 0x17, + 0x00, 0x21, 0x02, 0x07, 0x07, 0x00, 0x0d, 0x02, 0x17, 0x00, 0x0e, 0x00, + 0x02, 0x27, 0x07, 0x21, 0x00, 0x02, 0x2e, 0x17, 0x2e, 0x0d, 0x04, 0x38, + 0x1c, 0x02, 0x30, 0x07, 0x38, 0x0f, 0x02, 0x1e, 0x00, 0x29, 0x00, 0x02, + 0x09, 0x07, 0x11, 0x00, 0x02, 0x00, 0x1c, 0x01, 0x0f, 0x01, 0x38, 0x1c, + 0x1f, 0x4a, 0x10, 0x02, 0x5a, 0x04, 0x51, 0x09, 0x02, 0x6e, 0x00, 0x62, + 0x00, 0x02, 0x8c, 0x0d, 0x81, 0x00, 0x02, 0x97, 0x2f, 0x97, 0x19, 0x01, + 0x97, 0x36, 0x01, 0x4f, 0x36, 0x02, 0x59, 0x4e, 0x51, 0x46, 0x02, 0x71, + 0x57, 0x62, 0x57, 0x02, 0x82, 0x55, 0x7a, 0x57, 0x02, 0x93, 0x4e, 0x8b, + 0x53, 0x01, 0x93, 0x5d, 0x02, 0x82, 0x62, 0x8b, 0x61, 0x02, 0x71, 0x64, + 0x79, 0x64, 0x02, 0x58, 0x5f, 0x62, 0x64, 0x02, 0x47, 0x50, 0x4d, 0x5a, + 0x02, 0x38, 0x5f, 0x40, 0x5a, 0x02, 0x22, 0x64, 0x2e, 0x64, 0x02, 0x09, + 0x5c, 0x12, 0x64, 0x02, 0x00, 0x46, 0x00, 0x55, 0x02, 0x0b, 0x2e, 0x00, + 0x36, 0x02, 0x2b, 0x26, 0x16, 0x26, 0x01, 0x41, 0x26, 0x01, 0x41, 0x24, + 0x02, 0x3a, 0x14, 0x41, 0x1a, 0x02, 0x26, 0x0e, 0x33, 0x0e, 0x02, 0x16, + 0x10, 0x1e, 0x0e, 0x02, 0x08, 0x16, 0x0e, 0x12, 0x01, 0x08, 0x08, 0x02, + 0x18, 0x02, 0x10, 0x04, 0x02, 0x27, 0x00, 0x20, 0x00, 0x02, 0x3c, 0x04, + 0x34, 0x00, 0x02, 0x4a, 0x10, 0x46, 0x09, 0x0d, 0x1a, 0x00, 0x02, 0x22, + 0x0a, 0x1f, 0x06, 0x02, 0x24, 0x13, 0x24, 0x0f, 0x02, 0x1e, 0x1e, 0x24, + 0x1a, 0x02, 0x0f, 0x22, 0x1a, 0x22, 0x02, 0x08, 0x21, 0x0b, 0x22, 0x02, + 0x00, 0x20, 0x05, 0x21, 0x01, 0x00, 0x15, 0x02, 0x07, 0x17, 0x03, 0x16, + 0x02, 0x0e, 0x17, 0x0a, 0x17, 0x02, 0x16, 0x15, 0x13, 0x17, 0x02, 0x18, + 0x0f, 0x18, 0x13, 0x02, 0x16, 0x09, 0x18, 0x0d, 0x02, 0x10, 0x00, 0x14, + 0x06, 0x01, 0x1a, 0x00, 0x03, 0x11, 0x00, 0x01, 0x28, 0x20, 0x01, 0x1b, + 0x20, 0x01, 0x00, 0x00, 0x01, 0x11, 0x00, 0x06, 0x15, 0x00, 0x01, 0x22, + 0x00, 0x01, 0x36, 0x20, 0x01, 0x2a, 0x20, 0x01, 0x1b, 0x0b, 0x01, 0x0c, + 0x20, 0x01, 0x00, 0x20, 0x01, 0x15, 0x00, 0x03, 0x10, 0x00, 0x01, 0x29, + 0x20, 0x01, 0x1c, 0x20, 0x01, 0x00, 0x00, 0x01, 0x10, 0x00, 0x06, 0x15, + 0x00, 0x01, 0x21, 0x00, 0x01, 0x36, 0x20, 0x01, 0x2a, 0x20, 0x01, 0x1b, + 0x0b, 0x01, 0x0c, 0x20, 0x01, 0x00, 0x20, 0x01, 0x15, 0x00, 0x09, 0x2d, + 0x03, 0x02, 0x25, 0x01, 0x28, 0x02, 0x02, 0x1d, 0x00, 0x21, 0x00, 0x02, + 0x08, 0x09, 0x0f, 0x00, 0x02, 0x00, 0x23, 0x00, 0x13, 0x02, 0x07, 0x3c, + 0x00, 0x33, 0x02, 0x1b, 0x46, 0x0f, 0x46, 0x02, 0x2e, 0x3c, 0x27, 0x46, + 0x02, 0x36, 0x23, 0x36, 0x33, 0x02, 0x34, 0x11, 0x36, 0x19, 0x02, 0x2d, + 0x03, 0x31, 0x09, 0x14, 0x44, 0x24, 0x02, 0x52, 0x3c, 0x4e, 0x30, 0x02, + 0x58, 0x57, 0x58, 0x49, 0x02, 0x4c, 0x7a, 0x58, 0x6d, 0x02, 0x2c, 0x87, + 0x3f, 0x87, 0x02, 0x0d, 0x7a, 0x18, 0x87, 0x02, 0x00, 0x57, 0x00, 0x6d, + 0x02, 0x0c, 0x34, 0x00, 0x42, 0x02, 0x2a, 0x27, 0x18, 0x27, 0x02, 0x2e, + 0x27, 0x2c, 0x27, 0x02, 0x34, 0x28, 0x31, 0x28, 0x01, 0x26, 0x19, 0x01, + 0x0b, 0x22, 0x01, 0x09, 0x19, 0x01, 0x20, 0x12, 0x01, 0x11, 0x00, 0x01, + 0x24, 0x00, 0x01, 0x2f, 0x0c, 0x01, 0x4c, 0x03, 0x01, 0x4e, 0x0c, 0x01, + 0x35, 0x14, 0x01, 0x44, 0x24, 0x13, 0x1b, 0x13, 0x01, 0x17, 0x0e, 0x02, + 0x13, 0x0c, 0x15, 0x0d, 0x02, 0x11, 0x0b, 0x12, 0x0b, 0x02, 0x0d, 0x0e, + 0x0e, 0x0b, 0x02, 0x0b, 0x18, 0x0b, 0x11, 0x01, 0x00, 0x18, 0x02, 0x04, + 0x07, 0x00, 0x0d, 0x02, 0x11, 0x00, 0x09, 0x00, 0x02, 0x17, 0x02, 0x14, + 0x00, 0x02, 0x1c, 0x06, 0x19, 0x03, 0x01, 0x22, 0x0b, 0x02, 0x24, 0x0d, + 0x23, 0x0c, 0x02, 0x27, 0x0d, 0x26, 0x0d, 0x02, 0x2c, 0x0b, 0x2a, 0x0d, + 0x02, 0x2e, 0x00, 0x2d, 0x07, 0x01, 0x38, 0x00, 0x02, 0x34, 0x12, 0x38, + 0x0c, 0x02, 0x28, 0x18, 0x2f, 0x18, 0x02, 0x22, 0x17, 0x24, 0x18, 0x02, + 0x1b, 0x13, 0x1f, 0x15, 0x13, 0x1c, 0x13, 0x01, 0x17, 0x0e, 0x02, 0x14, + 0x0c, 0x15, 0x0d, 0x02, 0x11, 0x0b, 0x12, 0x0b, 0x02, 0x0c, 0x0e, 0x0e, + 0x0b, 0x02, 0x0b, 0x18, 0x0b, 0x11, 0x01, 0x00, 0x18, 0x02, 0x05, 0x07, + 0x00, 0x0d, 0x02, 0x10, 0x00, 0x09, 0x00, 0x02, 0x16, 0x02, 0x14, 0x00, + 0x02, 0x1c, 0x06, 0x19, 0x03, 0x01, 0x21, 0x0b, 0x02, 0x25, 0x0d, 0x23, + 0x0c, 0x02, 0x27, 0x0d, 0x26, 0x0d, 0x02, 0x2b, 0x0b, 0x2a, 0x0d, 0x02, + 0x2d, 0x00, 0x2d, 0x07, 0x01, 0x38, 0x00, 0x02, 0x34, 0x12, 0x38, 0x0c, + 0x02, 0x27, 0x18, 0x2f, 0x18, 0x02, 0x21, 0x17, 0x24, 0x18, 0x02, 0x1c, + 0x13, 0x1e, 0x15, 0x03, 0x00, 0x00, 0x01, 0x15, 0x00, 0x01, 0x15, 0x15, + 0x01, 0x00, 0x15, 0x01, 0x00, 0x00, 0x06, 0x2a, 0x00, 0x01, 0x00, 0x32, + 0x02, 0x08, 0x37, 0x04, 0x35, 0x02, 0x12, 0x39, 0x0c, 0x39, 0x02, 0x25, + 0x2f, 0x1f, 0x39, 0x02, 0x2d, 0x15, 0x2d, 0x25, 0x02, 0x2c, 0x08, 0x2d, + 0x0e, 0x02, 0x2a, 0x00, 0x2c, 0x04, 0x06, 0x2c, 0x07, 0x02, 0x24, 0x02, + 0x29, 0x03, 0x02, 0x1b, 0x00, 0x20, 0x00, 0x02, 0x07, 0x0a, 0x0e, 0x00, + 0x02, 0x00, 0x25, 0x00, 0x14, 0x02, 0x00, 0x30, 0x00, 0x2b, 0x02, 0x03, + 0x38, 0x02, 0x35, 0x01, 0x2c, 0x07, 0x11, 0x0d, 0x58, 0x02, 0x05, 0x49, + 0x08, 0x52, 0x02, 0x03, 0x37, 0x03, 0x41, 0x02, 0x0f, 0x12, 0x03, 0x20, + 0x02, 0x2f, 0x05, 0x1a, 0x05, 0x02, 0x3e, 0x07, 0x37, 0x05, 0x02, 0x4a, + 0x0e, 0x44, 0x0a, 0x01, 0x56, 0x00, 0x01, 0x5e, 0x07, 0x01, 0x51, 0x16, + 0x02, 0x58, 0x25, 0x56, 0x1c, 0x02, 0x5b, 0x38, 0x5b, 0x2d, 0x02, 0x4f, + 0x5c, 0x5b, 0x4f, 0x02, 0x2f, 0x69, 0x43, 0x69, 0x02, 0x20, 0x67, 0x27, + 0x69, 0x02, 0x14, 0x60, 0x19, 0x64, 0x01, 0x08, 0x6f, 0x01, 0x00, 0x68, + 0x01, 0x0d, 0x58, 0x03, 0x10, 0x00, 0x01, 0x28, 0x20, 0x01, 0x1b, 0x20, + 0x01, 0x00, 0x00, 0x01, 0x10, 0x00, 0x06, 0x15, 0x00, 0x01, 0x22, 0x00, + 0x01, 0x37, 0x20, 0x01, 0x2b, 0x20, 0x01, 0x1b, 0x0b, 0x01, 0x0d, 0x20, + 0x01, 0x00, 0x20, 0x01, 0x15, 0x00, 0x0c, 0x10, 0x76, 0x01, 0x10, 0xa8, + 0x01, 0x00, 0xa8, 0x01, 0x00, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x34, + 0x02, 0x1d, 0x27, 0x15, 0x2c, 0x02, 0x2f, 0x23, 0x24, 0x23, 0x02, 0x4a, + 0x31, 0x40, 0x23, 0x02, 0x55, 0x56, 0x55, 0x3f, 0x02, 0x4a, 0x7a, 0x55, + 0x6c, 0x02, 0x2f, 0x87, 0x40, 0x87, 0x02, 0x1d, 0x83, 0x24, 0x87, 0x02, + 0x10, 0x76, 0x15, 0x7f, 0x03, 0x10, 0x00, 0x01, 0x20, 0x00, 0x01, 0x0d, + 0x16, 0x01, 0x00, 0x16, 0x01, 0x10, 0x00, 0x03, 0x17, 0x00, 0x01, 0x28, + 0x00, 0x01, 0x0d, 0x20, 0x01, 0x00, 0x20, 0x01, 0x17, 0x00, 0x06, 0x12, + 0x16, 0x01, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x01, 0x1a, 0x0f, 0x01, 0x28, + 0x00, 0x01, 0x34, 0x00, 0x01, 0x22, 0x16, 0x01, 0x12, 0x16, 0x06, 0x15, + 0x20, 0x01, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x01, 0x1b, 0x15, 0x01, 0x2a, + 0x00, 0x01, 0x36, 0x00, 0x01, 0x21, 0x20, 0x01, 0x15, 0x20, 0x14, 0x46, + 0x34, 0x01, 0x46, 0x18, 0x01, 0x2a, 0x18, 0x01, 0x2a, 0x0d, 0x01, 0x46, + 0x0d, 0x01, 0x46, 0x00, 0x01, 0x55, 0x00, 0x01, 0x55, 0x0d, 0x01, 0x62, + 0x0d, 0x01, 0x62, 0x18, 0x01, 0x55, 0x18, 0x01, 0x55, 0x84, 0x01, 0x46, + 0x84, 0x01, 0x46, 0x76, 0x02, 0x39, 0x83, 0x41, 0x7f, 0x02, 0x27, 0x87, + 0x32, 0x87, 0x02, 0x0b, 0x7a, 0x16, 0x87, 0x02, 0x00, 0x56, 0x00, 0x6c, + 0x02, 0x0b, 0x31, 0x00, 0x3f, 0x02, 0x27, 0x23, 0x16, 0x23, 0x02, 0x39, + 0x27, 0x32, 0x23, 0x02, 0x46, 0x34, 0x41, 0x2c, 0x09, 0x00, 0x00, 0x01, + 0x0a, 0x00, 0x02, 0x0f, 0x07, 0x0b, 0x05, 0x02, 0x1a, 0x09, 0x13, 0x09, + 0x02, 0x26, 0x07, 0x21, 0x09, 0x02, 0x2b, 0x00, 0x2a, 0x05, 0x01, 0x34, + 0x00, 0x02, 0x2d, 0x0f, 0x34, 0x0a, 0x02, 0x1a, 0x14, 0x26, 0x14, 0x02, + 0x07, 0x0f, 0x0e, 0x14, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x09, 0x00, 0x00, + 0x01, 0x0b, 0x00, 0x02, 0x0f, 0x0a, 0x0b, 0x07, 0x02, 0x1b, 0x0d, 0x13, + 0x0d, 0x02, 0x26, 0x0a, 0x22, 0x0d, 0x02, 0x2c, 0x00, 0x2a, 0x07, 0x01, + 0x35, 0x00, 0x02, 0x2e, 0x12, 0x35, 0x0c, 0x02, 0x1b, 0x19, 0x27, 0x19, + 0x02, 0x08, 0x12, 0x0f, 0x19, 0x02, 0x00, 0x00, 0x01, 0x0c, 0x03, 0x00, + 0x00, 0x01, 0x11, 0x00, 0x01, 0x11, 0x12, 0x01, 0x00, 0x12, 0x01, 0x00, + 0x00, 0x0d, 0x13, 0x00, 0x01, 0x24, 0x00, 0x01, 0x24, 0x34, 0x01, 0x3f, + 0x21, 0x01, 0x45, 0x2b, 0x01, 0x24, 0x42, 0x01, 0x24, 0x70, 0x01, 0x62, + 0x70, 0x01, 0x62, 0x7e, 0x01, 0x13, 0x7e, 0x01, 0x13, 0x4e, 0x01, 0x06, + 0x57, 0x01, 0x00, 0x4e, 0x01, 0x13, 0x40, 0x01, 0x13, 0x00, 0x0b, 0x11, + 0x00, 0x01, 0x20, 0x00, 0x01, 0x20, 0x33, 0x01, 0x2b, 0x2c, 0x01, 0x31, + 0x34, 0x01, 0x20, 0x41, 0x01, 0x20, 0x84, 0x01, 0x11, 0x84, 0x01, 0x11, + 0x4c, 0x01, 0x06, 0x54, 0x01, 0x00, 0x4a, 0x01, 0x11, 0x3f, 0x01, 0x11, + 0x00, 0x0e, 0xa4, 0x00, 0x01, 0xa4, 0x0e, 0x01, 0x65, 0x0e, 0x01, 0x65, + 0x34, 0x01, 0xa1, 0x34, 0x01, 0xa1, 0x42, 0x01, 0x65, 0x42, 0x01, 0x65, + 0x70, 0x01, 0xa5, 0x70, 0x01, 0xa5, 0x7e, 0x01, 0x4b, 0x7e, 0x02, 0x13, + 0x6e, 0x26, 0x7e, 0x02, 0x00, 0x3f, 0x00, 0x5e, 0x02, 0x13, 0x10, 0x00, + 0x20, 0x02, 0x4b, 0x00, 0x26, 0x00, 0x01, 0xa4, 0x00, 0x06, 0x41, 0x00, + 0x01, 0x36, 0x00, 0x02, 0x0d, 0x0c, 0x1b, 0x00, 0x02, 0x00, 0x31, 0x00, + 0x18, 0x02, 0x0d, 0x56, 0x00, 0x4a, 0x02, 0x36, 0x62, 0x1b, 0x62, 0x01, + 0x41, 0x62, 0x01, 0x41, 0x00, 0x04, 0x37, 0x1c, 0x02, 0x31, 0x07, 0x37, + 0x0f, 0x02, 0x1e, 0x00, 0x29, 0x00, 0x02, 0x09, 0x07, 0x11, 0x00, 0x02, + 0x00, 0x1c, 0x01, 0x0f, 0x01, 0x37, 0x1c, 0x16, 0x9f, 0x2f, 0x01, 0x9f, + 0x36, 0x01, 0x58, 0x36, 0x02, 0x61, 0x4e, 0x59, 0x46, 0x02, 0x79, 0x57, + 0x6a, 0x57, 0x02, 0x8b, 0x55, 0x83, 0x57, 0x02, 0x9c, 0x4e, 0x94, 0x52, + 0x01, 0x9c, 0x5d, 0x02, 0x8a, 0x62, 0x93, 0x60, 0x02, 0x78, 0x64, 0x81, + 0x64, 0x02, 0x61, 0x5f, 0x6b, 0x64, 0x02, 0x50, 0x52, 0x57, 0x5b, 0x02, + 0x41, 0x5f, 0x4a, 0x5b, 0x02, 0x2c, 0x64, 0x38, 0x64, 0x02, 0x0c, 0x57, + 0x17, 0x64, 0x02, 0x00, 0x33, 0x00, 0x4a, 0x02, 0x0c, 0x0e, 0x00, 0x1b, + 0x02, 0x2c, 0x00, 0x17, 0x00, 0x02, 0x41, 0x05, 0x38, 0x00, 0x02, 0x4f, + 0x13, 0x4a, 0x0a, 0x02, 0x60, 0x05, 0x56, 0x0a, 0x02, 0x76, 0x00, 0x69, + 0x00, 0x02, 0x94, 0x0d, 0x89, 0x00, 0x02, 0x9f, 0x2f, 0x9f, 0x19, 0x0d, + 0x1a, 0x00, 0x02, 0x21, 0x0a, 0x1f, 0x06, 0x02, 0x23, 0x13, 0x23, 0x0f, + 0x02, 0x1e, 0x1e, 0x23, 0x1a, 0x02, 0x0f, 0x22, 0x19, 0x22, 0x02, 0x07, + 0x21, 0x0b, 0x22, 0x02, 0x00, 0x20, 0x04, 0x21, 0x01, 0x00, 0x15, 0x02, + 0x07, 0x17, 0x03, 0x16, 0x02, 0x0d, 0x17, 0x09, 0x17, 0x02, 0x15, 0x15, + 0x12, 0x17, 0x02, 0x18, 0x0f, 0x18, 0x13, 0x02, 0x16, 0x09, 0x18, 0x0d, + 0x02, 0x10, 0x00, 0x14, 0x06, 0x01, 0x1a, 0x00, 0x0d, 0x1a, 0x00, 0x02, + 0x22, 0x0a, 0x1f, 0x06, 0x02, 0x24, 0x13, 0x24, 0x0f, 0x02, 0x1e, 0x1e, + 0x24, 0x1a, 0x02, 0x0f, 0x22, 0x19, 0x22, 0x02, 0x08, 0x21, 0x0b, 0x22, + 0x02, 0x00, 0x20, 0x04, 0x21, 0x01, 0x00, 0x15, 0x02, 0x06, 0x17, 0x03, + 0x16, 0x02, 0x0e, 0x17, 0x0a, 0x17, 0x02, 0x15, 0x15, 0x13, 0x17, 0x02, + 0x17, 0x0f, 0x17, 0x13, 0x02, 0x16, 0x09, 0x17, 0x0d, 0x02, 0x10, 0x00, + 0x14, 0x06, 0x01, 0x1a, 0x00, 0x06, 0x11, 0x16, 0x01, 0x00, 0x00, 0x01, + 0x0b, 0x00, 0x01, 0x1a, 0x0f, 0x01, 0x27, 0x00, 0x01, 0x33, 0x00, 0x01, + 0x21, 0x16, 0x01, 0x11, 0x16, 0x06, 0x15, 0x20, 0x01, 0x00, 0x00, 0x01, + 0x0c, 0x00, 0x01, 0x1b, 0x15, 0x01, 0x2b, 0x00, 0x01, 0x36, 0x00, 0x01, + 0x22, 0x20, 0x01, 0x15, 0x20, 0x1b, 0x69, 0x04, 0x01, 0x69, 0x12, 0x02, + 0x63, 0x0e, 0x66, 0x0f, 0x02, 0x5c, 0x0c, 0x5f, 0x0c, 0x02, 0x4f, 0x12, + 0x54, 0x0c, 0x02, 0x48, 0x22, 0x4a, 0x17, 0x01, 0x43, 0x3d, 0x01, 0x5b, + 0x3d, 0x01, 0x5b, 0x49, 0x01, 0x40, 0x49, 0x01, 0x35, 0x7e, 0x02, 0x28, + 0xa0, 0x30, 0x97, 0x02, 0x11, 0xa8, 0x21, 0xa8, 0x02, 0x08, 0xa8, 0x0c, + 0xa8, 0x02, 0x00, 0xa5, 0x04, 0xa7, 0x01, 0x00, 0x97, 0x02, 0x08, 0x9b, + 0x04, 0x99, 0x02, 0x10, 0x9c, 0x0c, 0x9c, 0x02, 0x1d, 0x97, 0x18, 0x9c, + 0x02, 0x24, 0x82, 0x21, 0x91, 0x01, 0x30, 0x49, 0x01, 0x1b, 0x49, 0x01, + 0x1b, 0x3d, 0x01, 0x32, 0x3d, 0x01, 0x38, 0x21, 0x02, 0x43, 0x08, 0x3b, + 0x11, 0x02, 0x58, 0x00, 0x4c, 0x00, 0x02, 0x61, 0x01, 0x5d, 0x00, 0x02, + 0x69, 0x04, 0x65, 0x02, 0x09, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x02, 0x0f, + 0x0a, 0x0b, 0x07, 0x02, 0x1a, 0x0d, 0x13, 0x0d, 0x02, 0x26, 0x0a, 0x22, + 0x0d, 0x02, 0x2b, 0x00, 0x2a, 0x07, 0x01, 0x35, 0x00, 0x02, 0x2d, 0x12, + 0x34, 0x0c, 0x02, 0x1a, 0x19, 0x26, 0x19, 0x02, 0x07, 0x12, 0x0e, 0x19, + 0x02, 0x00, 0x00, 0x01, 0x0c, 0x07, 0x1a, 0x0d, 0x02, 0x16, 0x04, 0x1a, + 0x07, 0x02, 0x0d, 0x00, 0x13, 0x00, 0x02, 0x04, 0x04, 0x08, 0x00, 0x02, + 0x00, 0x0d, 0x00, 0x07, 0x02, 0x04, 0x16, 0x00, 0x13, 0x02, 0x0d, 0x1a, + 0x08, 0x1a, 0x02, 0x16, 0x16, 0x13, 0x1a, 0x02, 0x1a, 0x0d, 0x1a, 0x13, + 0x07, 0x2e, 0x17, 0x02, 0x28, 0x28, 0x2e, 0x21, 0x02, 0x17, 0x2f, 0x21, + 0x2f, 0x02, 0x07, 0x28, 0x0e, 0x2f, 0x02, 0x00, 0x17, 0x00, 0x21, 0x02, + 0x07, 0x07, 0x00, 0x0d, 0x02, 0x17, 0x00, 0x0e, 0x00, 0x02, 0x28, 0x07, + 0x21, 0x00, 0x02, 0x2e, 0x17, 0x2e, 0x0d, 0x0d, 0x09, 0x00, 0x01, 0x13, + 0x00, 0x02, 0x0d, 0x09, 0x0f, 0x06, 0x02, 0x0c, 0x10, 0x0c, 0x0d, 0x02, + 0x0e, 0x15, 0x0c, 0x13, 0x02, 0x15, 0x17, 0x11, 0x17, 0x02, 0x1a, 0x17, + 0x17, 0x17, 0x02, 0x20, 0x15, 0x1d, 0x16, 0x01, 0x20, 0x20, 0x02, 0x19, + 0x21, 0x1c, 0x21, 0x02, 0x14, 0x22, 0x17, 0x22, 0x02, 0x05, 0x1e, 0x0a, + 0x22, 0x02, 0x00, 0x13, 0x00, 0x1a, 0x02, 0x02, 0x0a, 0x00, 0x0f, 0x02, + 0x09, 0x00, 0x04, 0x06, 0x13, 0x1c, 0x13, 0x01, 0x17, 0x0e, 0x02, 0x13, + 0x0c, 0x15, 0x0d, 0x02, 0x11, 0x0b, 0x12, 0x0b, 0x02, 0x0d, 0x0e, 0x0e, + 0x0b, 0x02, 0x0b, 0x18, 0x0b, 0x11, 0x01, 0x00, 0x18, 0x02, 0x05, 0x07, + 0x00, 0x0d, 0x02, 0x11, 0x00, 0x09, 0x00, 0x02, 0x17, 0x02, 0x14, 0x00, + 0x02, 0x1c, 0x06, 0x1a, 0x03, 0x01, 0x22, 0x0b, 0x02, 0x24, 0x0d, 0x23, + 0x0c, 0x02, 0x27, 0x0d, 0x26, 0x0d, 0x02, 0x2c, 0x0b, 0x2b, 0x0d, 0x02, + 0x2e, 0x00, 0x2d, 0x07, 0x01, 0x38, 0x00, 0x02, 0x34, 0x12, 0x38, 0x0c, + 0x02, 0x28, 0x18, 0x2f, 0x18, 0x02, 0x22, 0x17, 0x24, 0x18, 0x02, 0x1c, + 0x13, 0x1f, 0x15, 0x03, 0x11, 0x00, 0x01, 0x21, 0x00, 0x01, 0x0c, 0x20, + 0x01, 0x00, 0x20, 0x01, 0x11, 0x00, 0x03, 0x10, 0x00, 0x01, 0x1f, 0x00, + 0x01, 0x0c, 0x20, 0x01, 0x00, 0x20, 0x01, 0x10, 0x00, 0x17, 0x77, 0x71, + 0x01, 0x77, 0x80, 0x01, 0x44, 0x80, 0x01, 0x44, 0x71, 0x02, 0x5b, 0x5b, + 0x53, 0x69, 0x02, 0x64, 0x3c, 0x64, 0x4d, 0x02, 0x59, 0x1b, 0x64, 0x28, + 0x02, 0x3b, 0x0f, 0x4e, 0x0f, 0x02, 0x1d, 0x1b, 0x29, 0x0f, 0x02, 0x13, + 0x3c, 0x13, 0x28, 0x02, 0x1b, 0x5b, 0x13, 0x4d, 0x02, 0x33, 0x71, 0x23, + 0x69, 0x01, 0x33, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x71, 0x01, 0x1b, + 0x71, 0x02, 0x07, 0x59, 0x0d, 0x66, 0x02, 0x01, 0x3d, 0x01, 0x4c, 0x02, + 0x11, 0x11, 0x01, 0x21, 0x02, 0x3b, 0x00, 0x22, 0x00, 0x02, 0x65, 0x11, + 0x55, 0x00, 0x02, 0x76, 0x3c, 0x76, 0x21, 0x02, 0x70, 0x59, 0x76, 0x4c, + 0x02, 0x5c, 0x71, 0x69, 0x65, 0x01, 0x77, 0x71, 0x1b, 0x65, 0x0d, 0x01, + 0x56, 0x0d, 0x01, 0x4d, 0x36, 0x02, 0x4b, 0x41, 0x4b, 0x3d, 0x02, 0x4a, + 0x47, 0x4a, 0x45, 0x02, 0x4c, 0x4e, 0x4a, 0x4b, 0x02, 0x53, 0x50, 0x4e, + 0x50, 0x02, 0x56, 0x50, 0x54, 0x50, 0x02, 0x5b, 0x4f, 0x58, 0x4f, 0x01, + 0x59, 0x5a, 0x02, 0x54, 0x5c, 0x56, 0x5b, 0x02, 0x50, 0x5c, 0x52, 0x5c, + 0x02, 0x40, 0x58, 0x45, 0x5c, 0x02, 0x3b, 0x4b, 0x3b, 0x53, 0x02, 0x3c, + 0x41, 0x3b, 0x47, 0x02, 0x40, 0x2d, 0x3d, 0x3b, 0x01, 0x47, 0x0d, 0x01, + 0x2b, 0x0d, 0x01, 0x1b, 0x5a, 0x01, 0x0b, 0x5a, 0x01, 0x1c, 0x0d, 0x01, + 0x18, 0x0d, 0x02, 0x11, 0x10, 0x14, 0x0d, 0x02, 0x0e, 0x19, 0x0e, 0x13, + 0x01, 0x00, 0x19, 0x02, 0x08, 0x05, 0x03, 0x0a, 0x02, 0x1d, 0x00, 0x0e, + 0x00, 0x01, 0x68, 0x00, 0x01, 0x65, 0x0d, 0x03, 0x00, 0x00, 0x01, 0x57, + 0x00, 0x01, 0x57, 0x0c, 0x01, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x01, 0xad, 0x00, 0x01, 0xad, 0x0c, 0x01, 0x00, 0x0c, 0x01, 0x00, + 0x00, 0x05, 0x11, 0x2a, 0x01, 0x00, 0x2a, 0x01, 0x00, 0x1b, 0x01, 0x0d, + 0x00, 0x01, 0x18, 0x00, 0x01, 0x11, 0x1b, 0x01, 0x11, 0x2a, 0x05, 0x07, + 0x00, 0x01, 0x19, 0x00, 0x01, 0x19, 0x0c, 0x01, 0x0b, 0x28, 0x01, 0x00, + 0x28, 0x01, 0x07, 0x0c, 0x01, 0x07, 0x00, 0x05, 0x07, 0x00, 0x01, 0x18, + 0x00, 0x01, 0x18, 0x0f, 0x01, 0x0b, 0x2a, 0x01, 0x00, 0x2a, 0x01, 0x07, + 0x0f, 0x01, 0x07, 0x00, 0x05, 0x12, 0x2a, 0x01, 0x00, 0x2a, 0x01, 0x00, + 0x1b, 0x01, 0x0e, 0x00, 0x01, 0x19, 0x00, 0x01, 0x12, 0x1b, 0x01, 0x12, + 0x2a, 0x0b, 0x1f, 0x00, 0x01, 0x2e, 0x00, 0x01, 0x2e, 0x24, 0x01, 0x4d, + 0x24, 0x01, 0x4d, 0x30, 0x01, 0x2e, 0x30, 0x01, 0x2e, 0x8f, 0x01, 0x1f, + 0x8f, 0x01, 0x1f, 0x30, 0x01, 0x00, 0x30, 0x01, 0x00, 0x24, 0x01, 0x1f, + 0x24, 0x01, 0x1f, 0x00, 0x13, 0x4d, 0x6b, 0x01, 0x2e, 0x6b, 0x01, 0x2e, + 0x8f, 0x01, 0x1f, 0x8f, 0x01, 0x1f, 0x6b, 0x01, 0x00, 0x6b, 0x01, 0x00, + 0x5f, 0x01, 0x1f, 0x5f, 0x01, 0x1f, 0x30, 0x01, 0x00, 0x30, 0x01, 0x00, + 0x24, 0x01, 0x1f, 0x24, 0x01, 0x1f, 0x00, 0x01, 0x2e, 0x00, 0x01, 0x2e, + 0x24, 0x01, 0x4d, 0x24, 0x01, 0x4d, 0x30, 0x01, 0x2e, 0x30, 0x01, 0x2e, + 0x5f, 0x01, 0x4d, 0x5f, 0x01, 0x4d, 0x6b, 0x07, 0x00, 0x19, 0x02, 0x07, + 0x07, 0x00, 0x0e, 0x02, 0x19, 0x00, 0x0f, 0x00, 0x02, 0x2b, 0x07, 0x24, + 0x00, 0x02, 0x33, 0x19, 0x33, 0x0e, 0x02, 0x2b, 0x2b, 0x33, 0x24, 0x02, + 0x19, 0x32, 0x24, 0x32, 0x02, 0x07, 0x2b, 0x0f, 0x32, 0x02, 0x00, 0x19, + 0x00, 0x24, 0x07, 0x10, 0x00, 0x02, 0x04, 0x06, 0x09, 0x00, 0x02, 0x00, + 0x18, 0x00, 0x0d, 0x02, 0x04, 0x29, 0x00, 0x22, 0x02, 0x10, 0x2f, 0x09, + 0x2f, 0x02, 0x1b, 0x29, 0x17, 0x2f, 0x02, 0x1f, 0x18, 0x1f, 0x22, 0x02, + 0x1b, 0x06, 0x1f, 0x0d, 0x02, 0x10, 0x00, 0x17, 0x00, 0x07, 0x1d, 0x00, + 0x02, 0x33, 0x09, 0x2a, 0x00, 0x02, 0x3a, 0x23, 0x3a, 0x13, 0x02, 0x33, + 0x3c, 0x3a, 0x33, 0x02, 0x1d, 0x45, 0x2a, 0x45, 0x02, 0x08, 0x3c, 0x0f, + 0x45, 0x02, 0x00, 0x23, 0x00, 0x33, 0x02, 0x08, 0x09, 0x00, 0x12, 0x02, + 0x1d, 0x00, 0x10, 0x00, 0x25, 0x63, 0x0c, 0x01, 0x63, 0x1e, 0x02, 0x54, + 0x11, 0x5b, 0x15, 0x02, 0x44, 0x0d, 0x4d, 0x0d, 0x02, 0x2e, 0x16, 0x37, + 0x0d, 0x02, 0x23, 0x2e, 0x26, 0x1e, 0x01, 0x54, 0x2e, 0x01, 0x4f, 0x39, + 0x01, 0x22, 0x39, 0x02, 0x21, 0x3d, 0x21, 0x3b, 0x02, 0x21, 0x41, 0x21, + 0x3f, 0x02, 0x21, 0x46, 0x21, 0x44, 0x02, 0x22, 0x4a, 0x21, 0x48, 0x01, + 0x47, 0x4a, 0x01, 0x42, 0x54, 0x01, 0x23, 0x54, 0x02, 0x2e, 0x6d, 0x26, + 0x65, 0x02, 0x44, 0x76, 0x37, 0x76, 0x02, 0x54, 0x71, 0x4d, 0x76, 0x02, + 0x63, 0x65, 0x5b, 0x6e, 0x01, 0x63, 0x77, 0x02, 0x53, 0x80, 0x5b, 0x7d, + 0x02, 0x44, 0x83, 0x4c, 0x83, 0x02, 0x22, 0x77, 0x2f, 0x83, 0x02, 0x12, + 0x54, 0x15, 0x6b, 0x01, 0x00, 0x54, 0x01, 0x05, 0x4a, 0x01, 0x10, 0x4a, + 0x02, 0x10, 0x46, 0x10, 0x48, 0x02, 0x10, 0x41, 0x10, 0x44, 0x02, 0x10, + 0x3d, 0x10, 0x3f, 0x02, 0x10, 0x39, 0x10, 0x3b, 0x01, 0x00, 0x39, 0x01, + 0x05, 0x2e, 0x01, 0x12, 0x2e, 0x02, 0x22, 0x0c, 0x15, 0x19, 0x02, 0x44, + 0x00, 0x2f, 0x00, 0x02, 0x53, 0x03, 0x4c, 0x00, 0x02, 0x63, 0x0c, 0x5b, + 0x06, 0x0c, 0x0e, 0x00, 0x01, 0x1d, 0x15, 0x01, 0x2a, 0x00, 0x01, 0x39, + 0x00, 0x01, 0x39, 0x30, 0x01, 0x30, 0x30, 0x01, 0x30, 0x08, 0x01, 0x1f, + 0x21, 0x01, 0x1a, 0x21, 0x01, 0x09, 0x08, 0x01, 0x09, 0x30, 0x01, 0x00, + 0x30, 0x01, 0x00, 0x00, 0x01, 0x0e, 0x00, 0x07, 0x2c, 0x00, 0x01, 0x2c, + 0x08, 0x01, 0x1a, 0x08, 0x01, 0x1a, 0x30, 0x01, 0x11, 0x30, 0x01, 0x11, + 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x13, 0x37, + 0x3c, 0x02, 0x39, 0x2d, 0x38, 0x34, 0x02, 0x39, 0x20, 0x39, 0x26, 0x02, + 0x37, 0x0f, 0x39, 0x15, 0x02, 0x2d, 0x0a, 0x33, 0x0a, 0x02, 0x22, 0x0f, + 0x28, 0x0a, 0x02, 0x19, 0x14, 0x1c, 0x14, 0x02, 0x14, 0x12, 0x16, 0x14, + 0x02, 0x12, 0x0d, 0x12, 0x10, 0x02, 0x18, 0x04, 0x12, 0x08, 0x02, 0x27, + 0x00, 0x1e, 0x00, 0x02, 0x3f, 0x0d, 0x36, 0x00, 0x02, 0x48, 0x31, 0x48, + 0x1a, 0x02, 0x3c, 0x5f, 0x48, 0x4d, 0x02, 0x1e, 0x72, 0x30, 0x72, 0x02, + 0x09, 0x6a, 0x11, 0x72, 0x02, 0x00, 0x53, 0x00, 0x60, 0x02, 0x09, 0x37, + 0x00, 0x42, 0x02, 0x20, 0x2c, 0x13, 0x2c, 0x02, 0x2e, 0x30, 0x28, 0x2c, + 0x02, 0x37, 0x3c, 0x34, 0x33, 0x07, 0x24, 0x14, 0x02, 0x20, 0x05, 0x24, + 0x0b, 0x02, 0x16, 0x00, 0x1c, 0x00, 0x02, 0x06, 0x0b, 0x0c, 0x00, 0x02, + 0x00, 0x27, 0x00, 0x16, 0x02, 0x04, 0x36, 0x00, 0x32, 0x02, 0x0f, 0x3c, + 0x08, 0x3c, 0x02, 0x1d, 0x31, 0x18, 0x3c, 0x02, 0x24, 0x14, 0x24, 0x26, + 0x02, 0x24, 0x00, 0x01, 0x00, 0x5c, 0x01, 0x47, 0x5c, 0x01, 0x24, 0x00, + 0x03, 0x32, 0x00, 0x01, 0x45, 0x00, 0x01, 0x76, 0x7c, 0x01, 0x00, 0x7c, + 0x01, 0x32, 0x00, 0x07, 0x00, 0x00, 0x01, 0x69, 0x00, 0x01, 0x69, 0x9e, + 0x01, 0x55, 0x9e, 0x01, 0x55, 0x11, 0x01, 0x14, 0x11, 0x01, 0x14, 0x9e, + 0x01, 0x00, 0x9e, 0x01, 0x00, 0x00, 0x0b, 0x03, 0x00, 0x01, 0x6d, 0x00, + 0x01, 0x6d, 0x10, 0x01, 0x1c, 0x10, 0x01, 0x55, 0x4d, 0x01, 0x1a, 0x8d, + 0x01, 0x6f, 0x8d, 0x01, 0x6f, 0x9e, 0x01, 0x00, 0x9e, 0x01, 0x00, 0x91, + 0x01, 0x3e, 0x4d, 0x01, 0x03, 0x0f, 0x01, 0x03, 0x00, 0x03, 0x4f, 0x00, + 0x01, 0x5d, 0x00, 0x01, 0x0e, 0x83, 0x01, 0x00, 0x83, 0x01, 0x4f, 0x00, + 0x0a, 0x59, 0x00, 0x01, 0x69, 0x00, 0x01, 0x69, 0x08, 0x01, 0x60, 0x08, + 0x01, 0x2d, 0x91, 0x01, 0x28, 0x91, 0x01, 0x0d, 0x46, 0x01, 0x02, 0x4a, + 0x01, 0x00, 0x43, 0x01, 0x18, 0x3b, 0x01, 0x2e, 0x77, 0x01, 0x59, 0x00, + 0x07, 0x00, 0x19, 0x02, 0x0a, 0x24, 0x04, 0x20, 0x02, 0x17, 0x29, 0x10, + 0x29, 0x02, 0x25, 0x22, 0x1f, 0x29, 0x02, 0x2a, 0x14, 0x2a, 0x1d, 0x02, + 0x25, 0x06, 0x2a, 0x0b, 0x02, 0x19, 0x00, 0x21, 0x00, 0x02, 0x0c, 0x06, + 0x12, 0x00, 0x02, 0x00, 0x19, 0x07, 0x0b, 0x07, 0x2a, 0x0f, 0x02, 0x21, + 0x04, 0x26, 0x08, 0x02, 0x14, 0x00, 0x1a, 0x00, 0x02, 0x05, 0x06, 0x0b, + 0x00, 0x02, 0x00, 0x15, 0x00, 0x0b, 0x02, 0x05, 0x23, 0x00, 0x1d, 0x02, + 0x12, 0x28, 0x0a, 0x28, 0x02, 0x1e, 0x23, 0x18, 0x28, 0x02, 0x2a, 0x0f, + 0x24, 0x1d, 0x0f, 0x36, 0x2d, 0x02, 0x29, 0x3e, 0x30, 0x38, 0x02, 0x1b, + 0x43, 0x23, 0x43, 0x02, 0x07, 0x3a, 0x0f, 0x43, 0x02, 0x00, 0x22, 0x00, + 0x30, 0x02, 0x07, 0x0a, 0x00, 0x13, 0x02, 0x19, 0x01, 0x0d, 0x01, 0x02, + 0x28, 0x06, 0x21, 0x01, 0x02, 0x34, 0x17, 0x2e, 0x0b, 0x02, 0x41, 0x06, + 0x3a, 0x0b, 0x02, 0x50, 0x00, 0x47, 0x00, 0x02, 0x63, 0x0a, 0x5b, 0x00, + 0x02, 0x6b, 0x22, 0x6b, 0x14, 0x02, 0x64, 0x3a, 0x6b, 0x31, 0x02, 0x51, + 0x42, 0x5d, 0x42, 0x02, 0x43, 0x3e, 0x49, 0x42, 0x02, 0x36, 0x2d, 0x3d, + 0x38, 0x17, 0x16, 0x99, 0x02, 0x20, 0x5f, 0x1f, 0x99, 0x02, 0x20, 0x51, + 0x20, 0x56, 0x02, 0x2a, 0x13, 0x22, 0x25, 0x02, 0x43, 0x00, 0x31, 0x00, + 0x02, 0x4e, 0x03, 0x4a, 0x00, 0x02, 0x52, 0x0c, 0x52, 0x07, 0x02, 0x4f, + 0x14, 0x52, 0x11, 0x02, 0x48, 0x16, 0x4d, 0x16, 0x02, 0x42, 0x15, 0x44, + 0x16, 0x02, 0x3f, 0x10, 0x40, 0x13, 0x02, 0x3e, 0x0c, 0x3e, 0x0f, 0x02, + 0x3c, 0x09, 0x3e, 0x09, 0x02, 0x32, 0x3d, 0x33, 0x09, 0x02, 0x31, 0x57, + 0x31, 0x4e, 0x02, 0x28, 0x91, 0x2f, 0x7f, 0x02, 0x0f, 0xa3, 0x20, 0xa3, + 0x02, 0x04, 0x9f, 0x08, 0xa3, 0x02, 0x00, 0x96, 0x00, 0x9b, 0x02, 0x03, + 0x8e, 0x00, 0x91, 0x02, 0x0b, 0x8c, 0x05, 0x8c, 0x02, 0x10, 0x8d, 0x0e, + 0x8c, 0x02, 0x13, 0x90, 0x12, 0x8e, 0x02, 0x14, 0x96, 0x14, 0x92, 0x02, + 0x16, 0x99, 0x14, 0x99, 0x11, 0x6d, 0x00, 0x01, 0x6d, 0x0e, 0x02, 0x5c, + 0x19, 0x64, 0x15, 0x02, 0x4d, 0x1b, 0x55, 0x1b, 0x02, 0x37, 0x17, 0x43, + 0x1b, 0x02, 0x36, 0x16, 0x36, 0x16, 0x02, 0x34, 0x15, 0x35, 0x15, 0x02, + 0x1f, 0x10, 0x27, 0x10, 0x02, 0x10, 0x13, 0x17, 0x10, 0x02, 0x00, 0x1d, + 0x08, 0x17, 0x01, 0x00, 0x0e, 0x02, 0x10, 0x04, 0x09, 0x08, 0x02, 0x21, + 0x02, 0x19, 0x02, 0x02, 0x36, 0x06, 0x2a, 0x02, 0x02, 0x38, 0x07, 0x37, + 0x07, 0x02, 0x39, 0x08, 0x38, 0x08, 0x02, 0x4f, 0x0d, 0x47, 0x0d, 0x02, + 0x5d, 0x0a, 0x56, 0x0d, 0x02, 0x6d, 0x00, 0x64, 0x07, 0x11, 0x6d, 0x00, + 0x01, 0x6d, 0x0f, 0x02, 0x5c, 0x19, 0x64, 0x16, 0x02, 0x4d, 0x1c, 0x55, + 0x1c, 0x02, 0x37, 0x17, 0x43, 0x1c, 0x02, 0x36, 0x17, 0x36, 0x17, 0x02, + 0x34, 0x16, 0x35, 0x16, 0x02, 0x1f, 0x11, 0x27, 0x11, 0x02, 0x10, 0x14, + 0x17, 0x11, 0x02, 0x00, 0x1e, 0x08, 0x17, 0x01, 0x00, 0x0f, 0x02, 0x10, + 0x05, 0x09, 0x08, 0x02, 0x21, 0x02, 0x19, 0x02, 0x02, 0x36, 0x07, 0x2a, + 0x02, 0x02, 0x38, 0x08, 0x37, 0x08, 0x02, 0x39, 0x08, 0x38, 0x08, 0x02, + 0x4f, 0x0e, 0x47, 0x0e, 0x02, 0x5d, 0x0a, 0x56, 0x0e, 0x02, 0x6d, 0x00, + 0x64, 0x08, 0x13, 0x00, 0x1b, 0x01, 0x42, 0x1b, 0x01, 0x58, 0x00, 0x01, + 0x62, 0x09, 0x01, 0x53, 0x1b, 0x01, 0x6d, 0x1b, 0x01, 0x6d, 0x29, 0x01, + 0x47, 0x29, 0x01, 0x37, 0x3d, 0x01, 0x6d, 0x3d, 0x01, 0x6d, 0x4c, 0x01, + 0x2c, 0x4c, 0x01, 0x15, 0x66, 0x01, 0x0b, 0x5d, 0x01, 0x19, 0x4c, 0x01, + 0x00, 0x4c, 0x01, 0x00, 0x3d, 0x01, 0x25, 0x3d, 0x01, 0x36, 0x29, 0x01, + 0x00, 0x29, 0x01, 0x00, 0x1b, 0x06, 0x6d, 0x0f, 0x01, 0x1b, 0x27, 0x01, + 0x6d, 0x3e, 0x01, 0x6d, 0x4d, 0x01, 0x00, 0x2e, 0x01, 0x00, 0x20, 0x01, + 0x6d, 0x00, 0x01, 0x6d, 0x0f, 0x06, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x01, + 0x6d, 0x20, 0x01, 0x6d, 0x2e, 0x01, 0x00, 0x4d, 0x01, 0x00, 0x3e, 0x01, + 0x52, 0x27, 0x01, 0x00, 0x0f, 0x03, 0x6d, 0x00, 0x01, 0x6d, 0x0e, 0x01, + 0x00, 0x0e, 0x01, 0x00, 0x00, 0x01, 0x6d, 0x00, 0x03, 0x21, 0x00, 0x01, + 0x00, 0x45, 0x01, 0x21, 0x8b, 0x01, 0x41, 0x45, 0x01, 0x21, 0x00, 0x03, + 0x2a, 0x00, 0x01, 0x54, 0x5a, 0x01, 0x2a, 0xb4, 0x01, 0x00, 0x5a, 0x01, + 0x2a, 0x00, 0x13, 0x59, 0x25, 0x01, 0x59, 0x84, 0x01, 0x49, 0x84, 0x01, + 0x49, 0x32, 0x01, 0x1e, 0x32, 0x01, 0x1e, 0x84, 0x01, 0x0f, 0x84, 0x01, + 0x0f, 0x32, 0x01, 0x00, 0x32, 0x01, 0x00, 0x25, 0x01, 0x0f, 0x25, 0x01, + 0x0f, 0x1f, 0x02, 0x16, 0x08, 0x0f, 0x0f, 0x02, 0x2d, 0x00, 0x1d, 0x00, + 0x01, 0x3c, 0x00, 0x01, 0x3c, 0x0d, 0x01, 0x2d, 0x0d, 0x02, 0x22, 0x10, + 0x25, 0x0d, 0x02, 0x1e, 0x1d, 0x1e, 0x14, 0x01, 0x1e, 0x25, 0x01, 0x59, + 0x25, 0x13, 0x2d, 0x00, 0x01, 0x59, 0x00, 0x01, 0x59, 0x84, 0x01, 0x49, + 0x84, 0x01, 0x49, 0x0d, 0x01, 0x2d, 0x0d, 0x02, 0x22, 0x10, 0x25, 0x0d, + 0x02, 0x1e, 0x1d, 0x1e, 0x14, 0x01, 0x1e, 0x25, 0x01, 0x38, 0x25, 0x01, + 0x38, 0x32, 0x01, 0x1e, 0x32, 0x01, 0x1e, 0x84, 0x01, 0x0f, 0x84, 0x01, + 0x0f, 0x32, 0x01, 0x00, 0x32, 0x01, 0x00, 0x25, 0x01, 0x0f, 0x25, 0x01, + 0x0f, 0x1f, 0x02, 0x16, 0x08, 0x0f, 0x0f, 0x02, 0x2d, 0x00, 0x1e, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x80, 0xda, 0x6a, 0x00, 0x29, 0x41, 0xc5, 0x00, + 0x00, 0x00, 0xd4, 0x00, 0x09, 0x09, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4b, 0xbb, 0x47, + 0x00, 0x3a, 0x3d, 0xf2, 0x00, 0x00, 0x00, 0xe3, 0x00, 0x00, 0x69, 0x02, + 0x00, 0x00, 0x00, 0x5f, 0x6b, 0x51, 0x00, 0x31, 0x3d, 0x07, 0x01, 0x00, + 0x00, 0x07, 0x01, 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa4, 0xbb, 0x93, + 0x00, 0x2d, 0x3f, 0x25, 0x01, 0x00, 0x00, 0x16, 0x01, 0x2c, 0x30, 0x03, + 0x00, 0x00, 0x00, 0x80, 0xd5, 0x70, 0x00, 0x2e, 0x38, 0x7c, 0x01, 0x00, + 0x00, 0xed, 0x01, 0x10, 0x20, 0x07, 0x02, 0x2c, 0x52, 0x05, 0x00, 0x00, + 0x00, 0xbb, 0xbe, 0xa7, 0x00, 0x29, 0x3b, 0xb1, 0x02, 0x00, 0x00, 0xa2, + 0x02, 0x1b, 0x00, 0x77, 0x02, 0x0e, 0x0a, 0x4c, 0x02, 0x58, 0x3e, 0x21, + 0x02, 0x65, 0x49, 0x02, 0x00, 0x00, 0x00, 0xa2, 0xbe, 0x89, 0x00, 0x2b, + 0x3b, 0x00, 0x03, 0x00, 0x00, 0xdc, 0x02, 0x10, 0x3d, 0x01, 0x00, 0x00, + 0x00, 0x3f, 0x6b, 0x30, 0x00, 0x31, 0x3d, 0x07, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x55, 0xd2, 0x45, 0x00, 0x2f, 0x38, 0x83, 0x03, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0xd2, 0x45, 0x00, 0x2d, 0x38, 0xb4, + 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x71, 0x89, 0x58, 0x00, 0x25, + 0x3b, 0xe5, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x9f, 0xbb, 0x93, + 0x00, 0x32, 0x4e, 0x1e, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, + 0xd0, 0x38, 0x00, 0x2d, 0xa6, 0x45, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x56, 0x93, 0x3f, 0x00, 0x28, 0x84, 0x5a, 0x04, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x44, 0xbb, 0x38, 0x00, 0x32, 0xa6, 0x69, 0x04, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x5a, 0xcb, 0x3b, 0x00, 0x20, 0x3d, 0x78, + 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x83, 0xbe, 0x70, 0x00, 0x2b, + 0x3b, 0xb2, 0x04, 0x00, 0x00, 0x87, 0x04, 0x11, 0x0d, 0x01, 0x00, 0x00, + 0x00, 0x7e, 0xbb, 0x70, 0x00, 0x33, 0x3d, 0xdd, 0x04, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x7d, 0xbb, 0x70, 0x00, 0x2d, 0x3b, 0x01, 0x05, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0xbe, 0x70, 0x00, 0x2d, 0x3b, 0x63, + 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x84, 0xbb, 0x70, 0x00, 0x28, + 0x3d, 0xf9, 0x05, 0x00, 0x00, 0xed, 0x05, 0x0e, 0x0f, 0x01, 0x00, 0x00, + 0x00, 0x7f, 0xbe, 0x70, 0x00, 0x2d, 0x3d, 0x1d, 0x06, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x84, 0xbe, 0x70, 0x00, 0x2c, 0x3b, 0xad, 0x06, 0x00, + 0x00, 0x82, 0x06, 0x14, 0x3a, 0x01, 0x00, 0x00, 0x00, 0x80, 0xbb, 0x70, + 0x00, 0x2e, 0x3d, 0x03, 0x07, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x82, + 0xbe, 0x70, 0x00, 0x2b, 0x3b, 0x46, 0x07, 0x00, 0x00, 0x99, 0x07, 0x15, + 0x0d, 0x1b, 0x07, 0x12, 0x44, 0x02, 0x00, 0x00, 0x00, 0x82, 0xbe, 0x70, + 0x00, 0x2b, 0x3b, 0xc4, 0x07, 0x00, 0x00, 0x1a, 0x08, 0x11, 0x0d, 0x02, + 0x00, 0x00, 0x00, 0x46, 0xbb, 0x3b, 0x00, 0x34, 0x62, 0x45, 0x08, 0x00, + 0x00, 0x69, 0x04, 0x00, 0x44, 0x02, 0x00, 0x00, 0x00, 0x46, 0xd0, 0x3b, + 0x00, 0x2d, 0x62, 0x45, 0x08, 0x07, 0x00, 0x45, 0x04, 0x00, 0x44, 0x01, + 0x00, 0x00, 0x00, 0x9f, 0xb3, 0x93, 0x00, 0x32, 0x56, 0x54, 0x08, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x9f, 0x9e, 0x93, 0x00, 0x32, 0x6d, 0x6c, + 0x08, 0x00, 0x00, 0x7b, 0x08, 0x00, 0x22, 0x01, 0x00, 0x00, 0x00, 0x9f, + 0xb3, 0x93, 0x00, 0x32, 0x56, 0x8a, 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x70, 0xbb, 0x5d, 0x00, 0x2c, 0x3b, 0xa2, 0x08, 0x00, 0x00, 0xe3, + 0x00, 0x15, 0x6b, 0x02, 0x00, 0x00, 0x00, 0xc1, 0xda, 0xaf, 0x00, 0x2b, + 0x41, 0x3c, 0x09, 0x00, 0x00, 0x11, 0x09, 0x35, 0x33, 0x02, 0x00, 0x00, + 0x00, 0x95, 0xbb, 0x78, 0x00, 0x21, 0x3d, 0x22, 0x0a, 0x00, 0x00, 0x16, + 0x0a, 0x23, 0x11, 0x03, 0x00, 0x00, 0x00, 0x8a, 0xbb, 0x79, 0x00, 0x31, + 0x3d, 0x7d, 0x0a, 0x00, 0x00, 0x5d, 0x0a, 0x11, 0x0e, 0x3d, 0x0a, 0x11, + 0x42, 0x01, 0x00, 0x00, 0x00, 0x90, 0xbe, 0x7b, 0x00, 0x29, 0x3b, 0xb1, + 0x0a, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x9b, 0xbb, 0x87, 0x00, 0x31, + 0x3d, 0x2a, 0x0b, 0x00, 0x00, 0x0a, 0x0b, 0x11, 0x0e, 0x01, 0x00, 0x00, + 0x00, 0x82, 0xbb, 0x6f, 0x00, 0x31, 0x3d, 0x4a, 0x0b, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x79, 0xbb, 0x65, 0x00, 0x31, 0x3d, 0x71, 0x0b, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x98, 0xbe, 0x88, 0x00, 0x29, 0x3b, 0x92, + 0x0b, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x91, 0xbb, 0x84, 0x00, 0x31, + 0x3d, 0xf7, 0x0b, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0xbb, 0x34, + 0x00, 0x31, 0x3d, 0x1e, 0x0c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, + 0xde, 0x34, 0x00, 0x17, 0x3d, 0x2d, 0x0c, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x95, 0xbb, 0x73, 0x00, 0x31, 0x3d, 0x56, 0x0c, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x80, 0xbb, 0x62, 0x00, 0x31, 0x3d, 0x7a, 0x0c, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xa5, 0xbb, 0x98, 0x00, 0x31, 0x3d, 0x8f, + 0x0c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0xbb, 0x84, 0x00, 0x31, + 0x3d, 0xb9, 0x0c, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x9f, 0xbe, 0x8b, + 0x00, 0x29, 0x3b, 0x05, 0x0d, 0x00, 0x00, 0xda, 0x0c, 0x13, 0x0e, 0x02, + 0x00, 0x00, 0x00, 0x83, 0xbb, 0x6a, 0x00, 0x31, 0x3d, 0x50, 0x0d, 0x00, + 0x00, 0x30, 0x0d, 0x11, 0x0e, 0x02, 0x00, 0x00, 0x00, 0x9f, 0xd2, 0x8b, + 0x00, 0x29, 0x3b, 0x76, 0x0d, 0x00, 0x00, 0xda, 0x0c, 0x13, 0x0e, 0x02, + 0x00, 0x00, 0x00, 0x93, 0xbb, 0x7a, 0x00, 0x31, 0x3d, 0xb4, 0x0d, 0x00, + 0x00, 0xf7, 0x0d, 0x11, 0x0e, 0x01, 0x00, 0x00, 0x00, 0x84, 0xbe, 0x70, + 0x00, 0x2b, 0x3b, 0x17, 0x0e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x8a, + 0xbb, 0x6b, 0x00, 0x1f, 0x3d, 0x9e, 0x0e, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x90, 0xbe, 0x80, 0x00, 0x2f, 0x3d, 0xb9, 0x0e, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x95, 0xbb, 0x78, 0x00, 0x21, 0x3d, 0xf6, 0x0e, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xc6, 0xbb, 0xad, 0x00, 0x25, 0x3d, 0x0e, + 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x91, 0xbb, 0x78, 0x00, 0x25, + 0x3d, 0x38, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x8a, 0xbb, 0x6b, + 0x00, 0x1f, 0x3d, 0x5f, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x8f, + 0xbb, 0x78, 0x00, 0x27, 0x3d, 0x7d, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x53, 0xd2, 0x45, 0x00, 0x2f, 0x37, 0x9e, 0x0f, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x5a, 0xcb, 0x3b, 0x00, 0x20, 0x3d, 0xb9, 0x0f, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0xd2, 0x45, 0x00, 0x31, 0x37, 0xc8, + 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x9f, 0x6c, 0x93, 0x00, 0x32, + 0x3d, 0xe3, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0xe4, 0x58, + 0x00, 0x1e, 0xd8, 0xfb, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x57, + 0x50, 0x58, 0x00, 0x2e, 0x30, 0x0a, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x7b, 0xbe, 0x6c, 0x00, 0x2a, 0x5a, 0x40, 0x10, 0x00, 0x00, 0x19, + 0x10, 0x10, 0x32, 0x02, 0x00, 0x00, 0x00, 0x84, 0xbe, 0x70, 0x00, 0x2f, + 0x37, 0xc6, 0x10, 0x00, 0x00, 0x9b, 0x10, 0x10, 0x30, 0x01, 0x00, 0x00, + 0x00, 0x75, 0xbe, 0x61, 0x00, 0x29, 0x5a, 0x00, 0x11, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x7e, 0xbe, 0x70, 0x00, 0x29, 0x37, 0x59, 0x11, 0x00, + 0x00, 0x93, 0x11, 0x11, 0x30, 0x02, 0x00, 0x00, 0x00, 0x82, 0xbe, 0x6c, + 0x00, 0x29, 0x5a, 0xbe, 0x11, 0x00, 0x00, 0x06, 0x12, 0x11, 0x0e, 0x01, + 0x00, 0x00, 0x00, 0x60, 0xbb, 0x3e, 0x00, 0x24, 0x37, 0x20, 0x12, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x7e, 0xdf, 0x70, 0x00, 0x29, 0x5a, 0x8c, + 0x12, 0x00, 0x00, 0x61, 0x12, 0x11, 0x0d, 0x01, 0x00, 0x00, 0x00, 0x7f, + 0xbb, 0x6f, 0x00, 0x2f, 0x37, 0xee, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x40, 0xbb, 0x31, 0x00, 0x30, 0x37, 0x40, 0x13, 0x00, 0x00, 0x31, + 0x13, 0x00, 0x25, 0x02, 0x00, 0x00, 0x00, 0x40, 0xdf, 0x31, 0x00, 0x1c, + 0x37, 0x40, 0x13, 0x14, 0x00, 0x4f, 0x13, 0x00, 0x25, 0x01, 0x00, 0x00, + 0x00, 0x84, 0xbb, 0x66, 0x00, 0x2f, 0x37, 0x78, 0x13, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x40, 0xbb, 0x31, 0x00, 0x30, 0x37, 0x9c, 0x13, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xab, 0x00, 0x2f, 0x5a, 0xab, + 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0xbb, 0x6f, 0x00, 0x2f, + 0x5a, 0x1f, 0x14, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x81, 0xbe, 0x6c, + 0x00, 0x29, 0x5a, 0x8d, 0x14, 0x00, 0x00, 0x62, 0x14, 0x11, 0x0e, 0x02, + 0x00, 0x00, 0x00, 0x84, 0xdf, 0x70, 0x00, 0x2f, 0x5a, 0xb8, 0x14, 0x00, + 0x00, 0x9b, 0x10, 0x10, 0x0d, 0x02, 0x00, 0x00, 0x00, 0x7e, 0xdf, 0x70, + 0x00, 0x29, 0x5a, 0xf2, 0x14, 0x00, 0x00, 0x93, 0x11, 0x11, 0x0d, 0x01, + 0x00, 0x00, 0x00, 0x67, 0xbb, 0x48, 0x00, 0x2f, 0x5a, 0x2c, 0x15, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x72, 0xbe, 0x5b, 0x00, 0x29, 0x5a, 0x69, + 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xbb, 0x45, 0x00, 0x25, + 0x41, 0xf0, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7e, 0xbe, 0x6f, + 0x00, 0x2f, 0x5c, 0x31, 0x16, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x82, + 0xbb, 0x68, 0x00, 0x25, 0x5c, 0x74, 0x16, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0xa7, 0xbb, 0x8f, 0x00, 0x27, 0x5c, 0x8c, 0x16, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x81, 0xbb, 0x68, 0x00, 0x25, 0x5c, 0xb6, 0x16, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x82, 0xdf, 0x68, 0x00, 0x25, 0x5c, 0xdd, + 0x16, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x73, 0xbb, 0x5c, 0x00, 0x27, + 0x5c, 0x12, 0x17, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x79, 0xd7, 0x70, + 0x00, 0x36, 0x37, 0x33, 0x17, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x44, + 0xe4, 0x3b, 0x00, 0x36, 0x36, 0xad, 0x17, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x79, 0xd7, 0x70, 0x00, 0x36, 0x37, 0xbc, 0x17, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x9f, 0x94, 0x93, 0x00, 0x32, 0x76, 0x36, 0x18, 0x00, + 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x4b, 0xbb, 0x47, 0x00, 0x3a, 0x3d, 0x8f, 0x18, + 0x00, 0x00, 0x9e, 0x18, 0x00, 0x28, 0x02, 0x00, 0x00, 0x00, 0x79, 0xd6, + 0x70, 0x00, 0x2e, 0x42, 0xcd, 0x18, 0x00, 0x00, 0xb3, 0x18, 0x11, 0x26, + 0x01, 0x00, 0x00, 0x00, 0x7f, 0xbb, 0x70, 0x00, 0x2b, 0x3b, 0x27, 0x19, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x86, 0xb5, 0x70, 0x00, 0x28, 0x56, + 0x82, 0x19, 0x00, 0x00, 0xf9, 0x19, 0x18, 0x19, 0x01, 0x00, 0x00, 0x00, + 0x87, 0xbb, 0x70, 0x00, 0x27, 0x3d, 0x24, 0x1a, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x44, 0xd9, 0x3b, 0x00, 0x36, 0x42, 0x72, 0x1a, 0x00, 0x00, + 0x72, 0x1a, 0x00, 0x57, 0x02, 0x00, 0x00, 0x00, 0x6f, 0xcc, 0x58, 0x00, + 0x27, 0x3b, 0xac, 0x1a, 0x00, 0x00, 0x81, 0x1a, 0x0f, 0x31, 0x02, 0x00, + 0x00, 0x00, 0x64, 0x49, 0x58, 0x00, 0x32, 0x38, 0x53, 0x1b, 0x00, 0x00, + 0x53, 0x1b, 0x21, 0x00, 0x03, 0x00, 0x00, 0x00, 0xb6, 0xbb, 0xaf, 0x00, + 0x38, 0x3d, 0x62, 0x1b, 0x00, 0x00, 0xb5, 0x1b, 0x08, 0x09, 0x08, 0x1c, + 0x19, 0x19, 0x03, 0x00, 0x00, 0x00, 0x66, 0x93, 0x53, 0x00, 0x29, 0x3b, + 0x70, 0x1c, 0x00, 0x00, 0xcb, 0x1c, 0x0d, 0x23, 0x61, 0x1c, 0x02, 0x4e, + 0x02, 0x00, 0x00, 0x00, 0x7a, 0xb0, 0x6c, 0x00, 0x2d, 0x62, 0x0a, 0x1d, + 0x00, 0x00, 0xf2, 0x1c, 0x25, 0x00, 0x01, 0x00, 0x00, 0x00, 0x9f, 0xa3, + 0x93, 0x00, 0x32, 0x72, 0x22, 0x1d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x56, 0x93, 0x3f, 0x00, 0x28, 0x84, 0x5a, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0xb6, 0xbb, 0xaf, 0x00, 0x38, 0x3d, 0x62, 0x1b, 0x00, 0x00, + 0xb5, 0x1b, 0x08, 0x09, 0x57, 0x1d, 0x25, 0x1b, 0x37, 0x1d, 0x32, 0x24, + 0x01, 0x00, 0x00, 0x00, 0x64, 0x47, 0x58, 0x00, 0x32, 0x3a, 0x9a, 0x1d, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x66, 0x70, 0x58, 0x00, 0x30, 0x3b, + 0xd4, 0x1d, 0x00, 0x00, 0xa9, 0x1d, 0x0b, 0x0a, 0x02, 0x00, 0x00, 0x00, + 0x9f, 0xbb, 0x93, 0x00, 0x32, 0x4f, 0x09, 0x1e, 0x00, 0x00, 0x6c, 0x08, + 0x00, 0x5e, 0x01, 0x00, 0x00, 0x00, 0x5a, 0x83, 0x47, 0x00, 0x28, 0x3b, + 0x30, 0x1e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5d, 0x84, 0x47, 0x00, + 0x28, 0x3b, 0x7e, 0x1e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x68, 0x50, + 0x58, 0x00, 0x3f, 0x30, 0x08, 0x1f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x8a, 0xdf, 0x70, 0x00, 0x2f, 0x5c, 0x17, 0x1f, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x7b, 0xcc, 0x70, 0x00, 0x2d, 0x3d, 0x85, 0x1f, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x44, 0x8a, 0x38, 0x00, 0x32, 0x74, 0x45, 0x08, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5c, 0xdd, 0x58, 0x00, 0x38, 0xbb, + 0xb4, 0x1f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5c, 0x82, 0x47, 0x00, + 0x2b, 0x3c, 0xf9, 0x1f, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x93, + 0x53, 0x00, 0x28, 0x3b, 0x1d, 0x20, 0x00, 0x00, 0x48, 0x20, 0x0d, 0x09, + 0x61, 0x1c, 0x03, 0x4e, 0x02, 0x00, 0x00, 0x00, 0x7d, 0xb0, 0x6c, 0x00, + 0x30, 0x62, 0x73, 0x20, 0x00, 0x00, 0x73, 0x20, 0x25, 0x00, 0x04, 0x00, + 0x00, 0x00, 0xc3, 0xbe, 0xaa, 0x00, 0x2b, 0x3b, 0x8b, 0x20, 0x1b, 0x00, + 0xf9, 0x1f, 0x00, 0x01, 0xa6, 0x20, 0x5d, 0x39, 0x9a, 0x20, 0x65, 0x43, + 0x03, 0x00, 0x00, 0x00, 0xbd, 0xbe, 0xaa, 0x00, 0x2b, 0x3b, 0x8b, 0x20, + 0x1b, 0x00, 0xf9, 0x1f, 0x00, 0x01, 0xca, 0x20, 0x5f, 0x38, 0x04, 0x00, + 0x00, 0x00, 0xc3, 0xbe, 0xaa, 0x00, 0x28, 0x3b, 0x7e, 0x1e, 0x00, 0x00, + 0x8b, 0x20, 0x1e, 0x00, 0xa6, 0x20, 0x60, 0x39, 0x9a, 0x20, 0x68, 0x43, + 0x02, 0x00, 0x00, 0x00, 0x6f, 0xbe, 0x5d, 0x00, 0x2c, 0x3d, 0x8f, 0x18, + 0x1e, 0x00, 0x18, 0x21, 0x00, 0x21, 0x03, 0x00, 0x00, 0x00, 0x95, 0xbb, + 0x78, 0x00, 0x21, 0x1a, 0x87, 0x21, 0x26, 0x00, 0x22, 0x0a, 0x00, 0x23, + 0x16, 0x0a, 0x23, 0x34, 0x03, 0x00, 0x00, 0x00, 0x95, 0xbb, 0x78, 0x00, + 0x21, 0x1a, 0x96, 0x21, 0x2e, 0x00, 0x22, 0x0a, 0x00, 0x23, 0x16, 0x0a, + 0x23, 0x34, 0x03, 0x00, 0x00, 0x00, 0x95, 0xbb, 0x78, 0x00, 0x21, 0x1a, + 0xa5, 0x21, 0x20, 0x00, 0x22, 0x0a, 0x00, 0x23, 0x16, 0x0a, 0x23, 0x34, + 0x03, 0x00, 0x00, 0x00, 0x95, 0xbb, 0x78, 0x00, 0x21, 0x1b, 0xbd, 0x21, + 0x1e, 0x00, 0x22, 0x0a, 0x00, 0x22, 0x16, 0x0a, 0x23, 0x33, 0x04, 0x00, + 0x00, 0x00, 0x95, 0xbb, 0x78, 0x00, 0x21, 0x1d, 0x53, 0x1b, 0x21, 0x00, + 0x22, 0x22, 0x42, 0x00, 0x22, 0x0a, 0x00, 0x20, 0x16, 0x0a, 0x23, 0x31, + 0x03, 0x00, 0x00, 0x00, 0x95, 0xbb, 0x78, 0x00, 0x21, 0x1b, 0x5c, 0x22, + 0x00, 0x00, 0x31, 0x22, 0x2e, 0x0a, 0x16, 0x0a, 0x23, 0x33, 0x02, 0x00, + 0x00, 0x00, 0xbe, 0xbb, 0xab, 0x00, 0x21, 0x3d, 0x9c, 0x22, 0x00, 0x00, + 0xcf, 0x22, 0x24, 0x0d, 0x02, 0x00, 0x00, 0x00, 0x90, 0xdd, 0x7b, 0x00, + 0x29, 0x3b, 0xb1, 0x0a, 0x00, 0x00, 0xde, 0x22, 0x29, 0x80, 0x02, 0x00, + 0x00, 0x00, 0x82, 0xbb, 0x6f, 0x00, 0x31, 0x1a, 0x23, 0x23, 0x13, 0x00, + 0x4a, 0x0b, 0x00, 0x23, 0x02, 0x00, 0x00, 0x00, 0x82, 0xbb, 0x6f, 0x00, + 0x31, 0x1a, 0x96, 0x21, 0x1c, 0x00, 0x4a, 0x0b, 0x00, 0x23, 0x02, 0x00, + 0x00, 0x00, 0x82, 0xbb, 0x6f, 0x00, 0x31, 0x1a, 0x32, 0x23, 0x0e, 0x00, + 0x4a, 0x0b, 0x00, 0x23, 0x03, 0x00, 0x00, 0x00, 0x82, 0xbb, 0x6f, 0x00, + 0x31, 0x1d, 0x53, 0x1b, 0x0f, 0x00, 0x22, 0x22, 0x2f, 0x00, 0x4a, 0x0b, + 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x45, 0xbb, 0x34, 0x00, 0x25, 0x1a, + 0x23, 0x23, 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x23, 0x02, 0x00, 0x00, 0x00, + 0x4e, 0xbb, 0x34, 0x00, 0x2d, 0x1a, 0x4a, 0x23, 0x00, 0x00, 0x1e, 0x0c, + 0x04, 0x23, 0x02, 0x00, 0x00, 0x00, 0x53, 0xbb, 0x34, 0x00, 0x20, 0x1a, + 0x59, 0x23, 0x00, 0x00, 0x1e, 0x0c, 0x11, 0x23, 0x03, 0x00, 0x00, 0x00, + 0x53, 0xbb, 0x34, 0x00, 0x21, 0x1d, 0x53, 0x1b, 0x00, 0x00, 0x53, 0x1b, + 0x21, 0x00, 0x1e, 0x0c, 0x10, 0x20, 0x02, 0x00, 0x00, 0x00, 0x9c, 0xbb, + 0x88, 0x00, 0x21, 0x3d, 0x71, 0x23, 0x00, 0x00, 0x9d, 0x23, 0x22, 0x0e, + 0x02, 0x00, 0x00, 0x00, 0x90, 0xbb, 0x84, 0x00, 0x31, 0x1b, 0xc9, 0x23, + 0x13, 0x00, 0xb9, 0x0c, 0x00, 0x22, 0x03, 0x00, 0x00, 0x00, 0x9f, 0xbe, + 0x8b, 0x00, 0x29, 0x1a, 0x2e, 0x24, 0x26, 0x00, 0x05, 0x0d, 0x00, 0x21, + 0xda, 0x0c, 0x13, 0x2f, 0x03, 0x00, 0x00, 0x00, 0x9f, 0xbe, 0x8b, 0x00, + 0x29, 0x1a, 0x4a, 0x23, 0x2f, 0x00, 0x05, 0x0d, 0x00, 0x21, 0xda, 0x0c, + 0x13, 0x2f, 0x03, 0x00, 0x00, 0x00, 0x9f, 0xbe, 0x8b, 0x00, 0x29, 0x1a, + 0x3d, 0x24, 0x22, 0x00, 0x05, 0x0d, 0x00, 0x21, 0xda, 0x0c, 0x13, 0x2f, + 0x03, 0x00, 0x00, 0x00, 0x9f, 0xbe, 0x8b, 0x00, 0x29, 0x1b, 0x55, 0x24, + 0x20, 0x00, 0x05, 0x0d, 0x00, 0x20, 0xda, 0x0c, 0x13, 0x2e, 0x04, 0x00, + 0x00, 0x00, 0x9f, 0xbe, 0x8b, 0x00, 0x29, 0x1d, 0x53, 0x1b, 0x22, 0x00, + 0xba, 0x24, 0x44, 0x00, 0x05, 0x0d, 0x00, 0x1e, 0xda, 0x0c, 0x13, 0x2c, + 0x01, 0x00, 0x00, 0x00, 0x99, 0xb6, 0x93, 0x00, 0x38, 0x54, 0xc9, 0x24, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xa0, 0xc2, 0x8b, 0x00, 0x29, 0x37, + 0x38, 0x25, 0x00, 0x00, 0x14, 0x25, 0x13, 0x12, 0xf0, 0x24, 0x20, 0x26, + 0x02, 0x00, 0x00, 0x00, 0x90, 0xbe, 0x80, 0x00, 0x2f, 0x1a, 0x23, 0x23, + 0x1c, 0x00, 0xb9, 0x0e, 0x00, 0x23, 0x02, 0x00, 0x00, 0x00, 0x90, 0xbe, + 0x80, 0x00, 0x2f, 0x1a, 0x4a, 0x23, 0x24, 0x00, 0xb9, 0x0e, 0x00, 0x23, + 0x02, 0x00, 0x00, 0x00, 0x90, 0xbe, 0x80, 0x00, 0x2f, 0x1a, 0x89, 0x25, + 0x17, 0x00, 0xb9, 0x0e, 0x00, 0x23, 0x03, 0x00, 0x00, 0x00, 0x90, 0xbe, + 0x80, 0x00, 0x2f, 0x1d, 0xba, 0x24, 0x18, 0x00, 0x22, 0x22, 0x38, 0x00, + 0xb9, 0x0e, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x8a, 0xbb, 0x6b, 0x00, + 0x1f, 0x1a, 0x96, 0x21, 0x2a, 0x00, 0x5f, 0x0f, 0x00, 0x23, 0x02, 0x00, + 0x00, 0x00, 0x83, 0xbb, 0x6a, 0x00, 0x31, 0x3d, 0xa1, 0x25, 0x00, 0x00, + 0xcd, 0x25, 0x11, 0x24, 0x01, 0x00, 0x00, 0x00, 0x85, 0xbe, 0x6f, 0x00, + 0x2f, 0x37, 0xed, 0x25, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7b, 0xbe, + 0x6c, 0x00, 0x2a, 0x30, 0x0a, 0x10, 0x0b, 0x00, 0x40, 0x10, 0x00, 0x2a, + 0x19, 0x10, 0x10, 0x5c, 0x03, 0x00, 0x00, 0x00, 0x7b, 0xbe, 0x6c, 0x00, + 0x2a, 0x30, 0x8e, 0x26, 0x1d, 0x00, 0x40, 0x10, 0x00, 0x2a, 0x19, 0x10, + 0x10, 0x5c, 0x03, 0x00, 0x00, 0x00, 0x7b, 0xbe, 0x6c, 0x00, 0x2a, 0x30, + 0x9d, 0x26, 0x0d, 0x00, 0x40, 0x10, 0x00, 0x2a, 0x19, 0x10, 0x10, 0x5c, + 0x03, 0x00, 0x00, 0x00, 0x7b, 0xbe, 0x6c, 0x00, 0x2a, 0x34, 0xb5, 0x26, + 0x0c, 0x00, 0x40, 0x10, 0x00, 0x26, 0x19, 0x10, 0x10, 0x58, 0x04, 0x00, + 0x00, 0x00, 0x7b, 0xbe, 0x6c, 0x00, 0x2a, 0x38, 0x53, 0x1b, 0x0f, 0x00, + 0x53, 0x1b, 0x30, 0x00, 0x40, 0x10, 0x00, 0x22, 0x19, 0x10, 0x10, 0x54, + 0x04, 0x00, 0x00, 0x00, 0x7b, 0xbe, 0x6c, 0x00, 0x2a, 0x23, 0x3f, 0x27, + 0x11, 0x00, 0x14, 0x27, 0x1b, 0x0a, 0x40, 0x10, 0x00, 0x37, 0x19, 0x10, + 0x10, 0x69, 0x03, 0x00, 0x00, 0x00, 0xc1, 0xbe, 0xac, 0x00, 0x2a, 0x5a, + 0x84, 0x27, 0x00, 0x00, 0x6a, 0x27, 0x50, 0x0e, 0x19, 0x10, 0x10, 0x32, + 0x02, 0x00, 0x00, 0x00, 0x75, 0xdd, 0x61, 0x00, 0x29, 0x5a, 0x00, 0x11, + 0x00, 0x00, 0x1b, 0x28, 0x1b, 0x61, 0x03, 0x00, 0x00, 0x00, 0x82, 0xbe, + 0x6c, 0x00, 0x29, 0x30, 0x60, 0x28, 0x11, 0x00, 0xbe, 0x11, 0x00, 0x2a, + 0x06, 0x12, 0x11, 0x38, 0x03, 0x00, 0x00, 0x00, 0x82, 0xbe, 0x6c, 0x00, + 0x29, 0x30, 0x08, 0x1f, 0x22, 0x00, 0xbe, 0x11, 0x00, 0x2a, 0x06, 0x12, + 0x11, 0x38, 0x03, 0x00, 0x00, 0x00, 0x82, 0xbe, 0x6c, 0x00, 0x29, 0x30, + 0x6f, 0x28, 0x13, 0x00, 0xbe, 0x11, 0x00, 0x2a, 0x06, 0x12, 0x11, 0x38, + 0x04, 0x00, 0x00, 0x00, 0x82, 0xbe, 0x6c, 0x00, 0x29, 0x38, 0x53, 0x1b, + 0x15, 0x00, 0x53, 0x1b, 0x36, 0x00, 0xbe, 0x11, 0x00, 0x22, 0x06, 0x12, + 0x11, 0x30, 0x02, 0x00, 0x00, 0x00, 0x44, 0xbb, 0x31, 0x00, 0x1b, 0x30, + 0x87, 0x28, 0x00, 0x00, 0x31, 0x13, 0x15, 0x2c, 0x02, 0x00, 0x00, 0x00, + 0x55, 0xbb, 0x31, 0x00, 0x2c, 0x30, 0x08, 0x1f, 0x00, 0x00, 0x31, 0x13, + 0x04, 0x2c, 0x02, 0x00, 0x00, 0x00, 0x53, 0xbb, 0x31, 0x00, 0x1d, 0x30, + 0x96, 0x28, 0x00, 0x00, 0x31, 0x13, 0x13, 0x2c, 0x03, 0x00, 0x00, 0x00, + 0x51, 0xbb, 0x31, 0x00, 0x1f, 0x38, 0x53, 0x1b, 0x00, 0x00, 0x53, 0x1b, + 0x21, 0x00, 0x31, 0x13, 0x11, 0x24, 0x02, 0x00, 0x00, 0x00, 0x81, 0xbe, + 0x6c, 0x00, 0x29, 0x37, 0xe3, 0x28, 0x00, 0x00, 0xae, 0x28, 0x11, 0x34, + 0x02, 0x00, 0x00, 0x00, 0x7f, 0xbb, 0x6f, 0x00, 0x2f, 0x34, 0x39, 0x29, + 0x0d, 0x00, 0x1f, 0x14, 0x00, 0x26, 0x03, 0x00, 0x00, 0x00, 0x81, 0xbe, + 0x6c, 0x00, 0x29, 0x30, 0x60, 0x28, 0x0f, 0x00, 0x8d, 0x14, 0x00, 0x2a, + 0x62, 0x14, 0x11, 0x38, 0x03, 0x00, 0x00, 0x00, 0x81, 0xbe, 0x6c, 0x00, + 0x29, 0x30, 0x08, 0x1f, 0x20, 0x00, 0x8d, 0x14, 0x00, 0x2a, 0x62, 0x14, + 0x11, 0x38, 0x03, 0x00, 0x00, 0x00, 0x81, 0xbe, 0x6c, 0x00, 0x29, 0x30, + 0x6f, 0x28, 0x11, 0x00, 0x8d, 0x14, 0x00, 0x2a, 0x62, 0x14, 0x11, 0x38, + 0x03, 0x00, 0x00, 0x00, 0x81, 0xbe, 0x6c, 0x00, 0x29, 0x34, 0x98, 0x29, + 0x10, 0x00, 0x8d, 0x14, 0x00, 0x26, 0x62, 0x14, 0x11, 0x34, 0x04, 0x00, + 0x00, 0x00, 0x81, 0xbe, 0x6c, 0x00, 0x29, 0x38, 0x53, 0x1b, 0x13, 0x00, + 0x53, 0x1b, 0x34, 0x00, 0x8d, 0x14, 0x00, 0x22, 0x62, 0x14, 0x11, 0x30, + 0x03, 0x00, 0x00, 0x00, 0x9f, 0xaf, 0x93, 0x00, 0x32, 0x5b, 0xf7, 0x29, + 0x2c, 0x00, 0x6c, 0x08, 0x00, 0x23, 0xf7, 0x29, 0x2c, 0x3f, 0x03, 0x00, + 0x00, 0x00, 0x84, 0xc4, 0x6c, 0x00, 0x26, 0x55, 0x4e, 0x2a, 0x00, 0x00, + 0x2a, 0x2a, 0x14, 0x13, 0x06, 0x2a, 0x1d, 0x23, 0x02, 0x00, 0x00, 0x00, + 0x7e, 0xbe, 0x6f, 0x00, 0x2f, 0x30, 0x9f, 0x2a, 0x0a, 0x00, 0x31, 0x16, + 0x00, 0x2c, 0x02, 0x00, 0x00, 0x00, 0x7e, 0xbe, 0x6f, 0x00, 0x2f, 0x30, + 0x08, 0x1f, 0x1b, 0x00, 0x31, 0x16, 0x00, 0x2c, 0x02, 0x00, 0x00, 0x00, + 0x7e, 0xbe, 0x6f, 0x00, 0x2f, 0x30, 0xae, 0x2a, 0x0b, 0x00, 0x31, 0x16, + 0x00, 0x2c, 0x03, 0x00, 0x00, 0x00, 0x7e, 0xbe, 0x6f, 0x00, 0x2f, 0x38, + 0x22, 0x22, 0x0d, 0x00, 0x53, 0x1b, 0x2f, 0x00, 0x31, 0x16, 0x00, 0x24, + 0x02, 0x00, 0x00, 0x00, 0x82, 0xdf, 0x68, 0x00, 0x25, 0x30, 0x08, 0x1f, + 0x22, 0x00, 0xdd, 0x16, 0x00, 0x2c, 0x02, 0x00, 0x00, 0x00, 0x84, 0xdf, + 0x70, 0x00, 0x2f, 0x37, 0xc6, 0x2a, 0x00, 0x00, 0x9b, 0x10, 0x10, 0x30, + 0x03, 0x00, 0x00, 0x00, 0x82, 0xdf, 0x68, 0x00, 0x25, 0x38, 0x53, 0x1b, + 0x15, 0x00, 0x22, 0x22, 0x36, 0x00, 0xdd, 0x16, 0x00, 0x24, 0x85, 0x02, + 0x00, 0x00, 0x00, 0x90, 0xbe, 0x7b, 0x00, 0x29, 0x1a, 0x00, 0x2b, 0x30, + 0x00, 0xb1, 0x0a, 0x00, 0x21, 0x02, 0x00, 0x00, 0x00, 0x75, 0xbe, 0x61, + 0x00, 0x29, 0x30, 0x0f, 0x2b, 0x22, 0x00, 0x00, 0x11, 0x00, 0x2a, 0x83, + 0x02, 0x00, 0x00, 0x00, 0x90, 0xbe, 0x7b, 0x00, 0x29, 0x1a, 0x1e, 0x2b, + 0x22, 0x00, 0xb1, 0x0a, 0x00, 0x21, 0x02, 0x00, 0x00, 0x00, 0x75, 0xbe, + 0x61, 0x00, 0x29, 0x30, 0x36, 0x2b, 0x13, 0x00, 0x00, 0x11, 0x00, 0x2a, + 0x82, 0x02, 0x00, 0x00, 0x00, 0x8b, 0xbe, 0x70, 0x00, 0x29, 0x37, 0x4e, + 0x2b, 0x00, 0x00, 0x93, 0x11, 0x11, 0x30, 0x8b, 0x02, 0x00, 0x00, 0x00, + 0x98, 0xbe, 0x88, 0x00, 0x29, 0x1a, 0xa0, 0x2b, 0x20, 0x00, 0x92, 0x0b, + 0x00, 0x21, 0x03, 0x00, 0x00, 0x00, 0x7e, 0xdf, 0x70, 0x00, 0x29, 0x33, + 0xd1, 0x2b, 0x13, 0x00, 0x8c, 0x12, 0x00, 0x27, 0x61, 0x12, 0x11, 0x34, + 0x8f, 0x02, 0x00, 0x00, 0x00, 0x42, 0xbb, 0x34, 0x00, 0x31, 0x1c, 0x02, + 0x2c, 0x00, 0x00, 0x1e, 0x0c, 0x00, 0x21, 0x01, 0x00, 0x00, 0x00, 0x40, + 0xbb, 0x31, 0x00, 0x30, 0x5c, 0x31, 0x13, 0x00, 0x00, 0x8e, 0x01, 0x00, + 0x00, 0x00, 0x81, 0xbb, 0x63, 0x00, 0x1f, 0x3d, 0x11, 0x2c, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x51, 0xbb, 0x32, 0x00, 0x20, 0x37, 0x3e, 0x2c, + 0x00, 0x00, 0x8e, 0x02, 0x00, 0x00, 0x00, 0xce, 0xbb, 0xbc, 0x00, 0x29, + 0x3d, 0x65, 0x2c, 0x00, 0x00, 0x9d, 0x2c, 0x13, 0x0e, 0x03, 0x00, 0x00, + 0x00, 0xc8, 0xbe, 0xb4, 0x00, 0x29, 0x5a, 0xd7, 0x2c, 0x00, 0x00, 0x62, + 0x14, 0x11, 0x0e, 0xbd, 0x2c, 0x58, 0x0e, 0x89, 0x02, 0x00, 0x00, 0x00, + 0x84, 0xdd, 0x70, 0x00, 0x2b, 0x3b, 0x17, 0x0e, 0x00, 0x00, 0x47, 0x2d, + 0x19, 0x80, 0x02, 0x00, 0x00, 0x00, 0x72, 0xdd, 0x5b, 0x00, 0x29, 0x5a, + 0x69, 0x15, 0x00, 0x00, 0x8c, 0x2d, 0x11, 0x61, 0x02, 0x00, 0x00, 0x00, + 0x84, 0xbe, 0x70, 0x00, 0x2b, 0x1a, 0x1e, 0x2b, 0x12, 0x00, 0x17, 0x0e, + 0x00, 0x21, 0x02, 0x00, 0x00, 0x00, 0x72, 0xbe, 0x5b, 0x00, 0x29, 0x30, + 0x36, 0x2b, 0x09, 0x00, 0x69, 0x15, 0x00, 0x2a, 0x95, 0x03, 0x00, 0x00, + 0x00, 0x8a, 0xbb, 0x6b, 0x00, 0x1f, 0x1d, 0x53, 0x1b, 0x1d, 0x00, 0x53, + 0x1b, 0x3e, 0x00, 0x5f, 0x0f, 0x00, 0x20, 0x83, 0x02, 0x00, 0x00, 0x00, + 0x8f, 0xbb, 0x78, 0x00, 0x27, 0x1a, 0xd1, 0x2d, 0x1b, 0x00, 0x7d, 0x0f, + 0x00, 0x23, 0x02, 0x00, 0x00, 0x00, 0x73, 0xbb, 0x5c, 0x00, 0x27, 0x30, + 0xe9, 0x2d, 0x0b, 0x00, 0x12, 0x17, 0x00, 0x2c, 0x92, 0x01, 0x00, 0x00, + 0x00, 0x8c, 0xdf, 0x70, 0x00, 0x23, 0x37, 0x01, 0x2e, 0x00, 0x00, 0xc1, + 0x32, 0x01, 0x00, 0x00, 0x00, 0x66, 0x50, 0x58, 0x00, 0x30, 0x30, 0x96, + 0x28, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x66, 0x50, 0x58, 0x00, 0x30, + 0x30, 0x36, 0x2b, 0x00, 0x00, 0x8f, 0x01, 0x00, 0x00, 0x00, 0x66, 0x4c, + 0x58, 0x00, 0x31, 0x33, 0x78, 0x2e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x54, 0x49, 0x58, 0x00, 0x42, 0x38, 0x22, 0x22, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x62, 0x52, 0x58, 0x00, 0x34, 0x23, 0xd4, 0x2e, 0x00, 0x00, + 0xa9, 0x2e, 0x0a, 0x0a, 0x01, 0x00, 0x00, 0x00, 0x5c, 0xdd, 0x58, 0x00, + 0x3c, 0xbb, 0xff, 0x2e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x67, 0x4c, + 0x58, 0x00, 0x2f, 0x34, 0x44, 0x2f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x70, 0x50, 0x58, 0x00, 0x34, 0x30, 0xb2, 0x2f, 0x00, 0x00, 0xa3, 0x2f, + 0x1b, 0x00, 0xc0, 0xca, 0x01, 0x00, 0x00, 0x00, 0x9e, 0xbb, 0x86, 0x00, + 0x27, 0x3b, 0xc1, 0x2f, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x85, + 0xbd, 0x68, 0x00, 0x1d, 0x61, 0x2c, 0x30, 0x00, 0x00, 0xdc, 0x51, 0x01, + 0x00, 0x00, 0x00, 0x77, 0x92, 0x58, 0x00, 0x20, 0x86, 0xa3, 0x30, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xcd, 0x92, 0xaf, 0x00, 0x20, 0x86, 0xb2, + 0x30, 0x00, 0x00, 0x82, 0x01, 0x00, 0x00, 0x00, 0x47, 0x67, 0x38, 0x00, + 0x2f, 0x3d, 0xc1, 0x30, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x65, + 0x38, 0x00, 0x2f, 0x3d, 0xd6, 0x30, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0xd0, 0x38, 0x00, 0x2f, 0xa6, 0xeb, 0x30, 0x00, 0x00, 0x80, 0x02, + 0x00, 0x00, 0x00, 0x6a, 0x67, 0x5b, 0x00, 0x2f, 0x3d, 0xc1, 0x30, 0x00, + 0x00, 0x00, 0x31, 0x22, 0x00, 0x02, 0x00, 0x00, 0x00, 0x6a, 0x67, 0x5b, + 0x00, 0x2f, 0x3d, 0xeb, 0x30, 0x00, 0x00, 0x45, 0x04, 0x22, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x6a, 0xd0, 0x5b, 0x00, 0x2f, 0xa6, 0xeb, 0x30, 0x00, + 0x00, 0x45, 0x04, 0x22, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x72, 0xcc, + 0x58, 0x00, 0x25, 0x3d, 0x15, 0x31, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x72, 0xcc, 0x58, 0x00, 0x25, 0x3d, 0x3c, 0x31, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x6d, 0x94, 0x68, 0x00, 0x3a, 0x62, 0x7b, 0x31, 0x00, 0x00, + 0x82, 0x03, 0x00, 0x00, 0x00, 0xb9, 0xbb, 0xaf, 0x00, 0x34, 0xa6, 0x69, + 0x04, 0x00, 0x00, 0x69, 0x04, 0x3a, 0x00, 0xe3, 0x00, 0x74, 0x00, 0x88, + 0x07, 0x00, 0x00, 0x00, 0xff, 0xbe, 0xec, 0x00, 0x29, 0x3b, 0xb1, 0x02, + 0x00, 0x00, 0xa2, 0x02, 0x1b, 0x00, 0x77, 0x02, 0x0e, 0x0a, 0x4c, 0x02, + 0x58, 0x3e, 0xd1, 0x31, 0x9c, 0x3e, 0x21, 0x02, 0x65, 0x49, 0xa6, 0x31, + 0xa9, 0x49, 0x87, 0x01, 0x00, 0x00, 0x00, 0x55, 0xb0, 0x46, 0x00, 0x2d, + 0x62, 0x0a, 0x1d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x58, 0xb0, 0x46, + 0x00, 0x30, 0x62, 0x73, 0x20, 0x00, 0x00, 0xc0, 0x70, 0x01, 0x00, 0x00, + 0x00, 0x83, 0xbe, 0x70, 0x00, 0x20, 0x3b, 0xfc, 0x31, 0x00, 0x00, 0xc0, + 0x74, 0x02, 0x00, 0x00, 0x00, 0xa8, 0x6d, 0xaf, 0x00, 0x39, 0x3d, 0xcb, + 0x32, 0x00, 0x00, 0xa1, 0x32, 0x36, 0x00, 0xc0, 0xde, 0x02, 0x00, 0x00, + 0x00, 0x71, 0xbd, 0x5b, 0x00, 0x29, 0x4b, 0xe6, 0x32, 0x00, 0x00, 0x4d, + 0x33, 0x0f, 0x32, 0x82, 0x02, 0x00, 0x00, 0x00, 0x95, 0xbb, 0x75, 0x00, + 0x1f, 0x3f, 0x84, 0x33, 0x00, 0x00, 0x78, 0x33, 0x17, 0x11, 0x87, 0x01, + 0x00, 0x00, 0x00, 0x96, 0xdd, 0x85, 0x00, 0x2d, 0x3f, 0x93, 0x33, 0x00, + 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x91, 0xdd, 0x77, 0x00, 0x22, 0x3f, + 0xae, 0x33, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x9f, 0x8c, 0x93, 0x00, + 0x32, 0x7e, 0x6c, 0x08, 0x00, 0x00, 0x81, 0x01, 0x00, 0x00, 0x00, 0x5d, + 0xbe, 0x1e, 0x00, 0x00, 0x3b, 0xd5, 0x33, 0x00, 0x00, 0x82, 0x01, 0x00, + 0x00, 0x00, 0x44, 0x8a, 0x38, 0x00, 0x32, 0x74, 0x45, 0x08, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x8e, 0xbf, 0x70, 0x00, 0x25, 0x2e, 0xe4, 0x33, + 0x00, 0x00, 0x82, 0x03, 0x00, 0x00, 0x00, 0x9e, 0xa9, 0x92, 0x00, 0x33, + 0x66, 0x5e, 0x34, 0x00, 0x00, 0x08, 0x34, 0x3a, 0x0d, 0x33, 0x34, 0x06, + 0x0e, 0x8b, 0x01, 0x00, 0x00, 0x00, 0x76, 0xdb, 0x5b, 0x00, 0x24, 0x38, + 0xb1, 0x34, 0x00, 0x00, 0x9b, 0x02, 0x00, 0x00, 0x00, 0x9f, 0xa4, 0x93, + 0x00, 0x32, 0x65, 0x85, 0x35, 0x00, 0x00, 0x2c, 0x35, 0x00, 0x22, 0x96, + 0x01, 0x00, 0x00, 0x00, 0x9f, 0xb8, 0x93, 0x00, 0x32, 0x52, 0xde, 0x35, + 0x00, 0x00, 0x82, 0x02, 0x00, 0x00, 0x00, 0x9f, 0xbb, 0x93, 0x00, 0x32, + 0x56, 0x1d, 0x36, 0x00, 0x00, 0x6c, 0x08, 0x00, 0x57, 0x02, 0x00, 0x00, + 0x00, 0x9f, 0xbb, 0x93, 0x00, 0x32, 0x56, 0x35, 0x36, 0x00, 0x00, 0x4d, + 0x36, 0x00, 0x57, 0xc3, 0x63, 0x02, 0x00, 0x00, 0x00, 0x75, 0xe4, 0x57, + 0x00, 0x21, 0x30, 0x6b, 0x36, 0x00, 0x00, 0x5c, 0x36, 0x09, 0x14, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xd5, 0x35, 0x02, 0x00, 0x00, 0x00, 0x7d, + 0xbb, 0x6f, 0x00, 0x24, 0x37, 0x7a, 0x36, 0x00, 0x00, 0x40, 0x13, 0x49, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x7d, 0xbb, 0x6f, 0x00, 0x24, 0x37, 0xc1, + 0x36, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc4, + 0xfd, 0xa3, 0x00, 0x46, 0x01, 0x00, 0x89, 0x01, 0x00, 0xa5, 0x01, 0x00, + 0xa9, 0x01, 0x80, 0x00, 0xbc, 0x01, 0x00, 0x20, 0x02, 0x00, 0x36, 0x02, + 0x80, 0x00, 0x40, 0x02, 0x00, 0x4a, 0x02, 0x00, 0xc0, 0x02, 0x81, 0x00, + 0x15, 0x03, 0x00, 0x28, 0x03, 0x00, 0x86, 0x03, 0x00, 0x8d, 0x03, 0x00, + 0x0c, 0x04, 0x00, 0x1f, 0x04, 0x80, 0x00, 0xad, 0x04, 0x00, 0x11, 0x05, + 0x00, 0x6c, 0x05, 0x00, 0xa6, 0x05, 0x8b, 0x00, 0x22, 0x06, 0x83, 0x00, + 0x2c, 0x06, 0x81, 0x00, 0x5d, 0x06, 0x00, 0x67, 0x06, 0x81, 0x00, 0x74, + 0x06, 0x82, 0x00, 0xae, 0x06, 0x00, 0xb2, 0x06, 0x00, 0xb6, 0x06, 0x00, + 0xd8, 0x06, 0x84, 0x00, 0x46, 0x01, 0x80, 0x00, 0xa5, 0x01, 0x81, 0x00, + 0x15, 0x03, 0x8f, 0x00, 0x5d, 0x06, 0x04, 0x67, 0x06, 0xaa, 0x00, 0xdc, + 0x06, 0x00, 0xec, 0x06, 0x81, 0x01, 0x46, 0x01, 0x00, 0x15, 0x03, 0x83, + 0x00, 0x17, 0x07, 0x84, 0x00, 0xd8, 0x06, 0x00, 0xa6, 0x05, 0x88, 0x00, + 0xa2, 0x07, 0x80, 0x00, 0x46, 0x01, 0x80, 0x00, 0x46, 0x01, 0x85, 0x02, + 0x15, 0x03, 0x8f, 0x00, 0x0c, 0x04, 0x84, 0x00, 0x3c, 0x08, 0x00, 0xa6, + 0x05, 0x00, 0xd8, 0x06, 0x00, 0x46, 0x08, 0x88, 0x00, 0x20, 0x02, 0x81, + 0x00, 0x0c, 0x04, 0x80, 0x00, 0xa5, 0x01, 0x80, 0x00, 0xa5, 0x01, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x02, 0x15, 0x24, 0x00, 0x05, 0x37, 0x00, 0xf4, 0x39, 0x00, + 0xf7, 0x3a, 0x00, 0xf8, 0x3c, 0x00, 0xf4, 0x49, 0x00, 0xfb, 0x59, 0x00, + 0xf7, 0x5a, 0x00, 0xfb, 0x5c, 0x00, 0xf6, 0x62, 0x00, 0x05, 0xa9, 0x00, + 0xfb, 0xad, 0x00, 0x05, 0xae, 0x00, 0x05, 0xb4, 0x00, 0xeb, 0xb5, 0x00, + 0xec, 0xba, 0x00, 0xf6, 0xbb, 0x00, 0xf4, 0xc5, 0x00, 0x05, 0xc7, 0x00, + 0x05, 0xc9, 0x00, 0x05, 0xea, 0x00, 0xf4, 0xeb, 0x00, 0xf6, 0x08, 0x39, + 0x00, 0xfc, 0x3a, 0x00, 0xfb, 0x3c, 0x00, 0xf8, 0xa9, 0x00, 0xfc, 0xb4, + 0x00, 0xf8, 0xb5, 0x00, 0xf8, 0xbb, 0x00, 0xf8, 0xc5, 0x00, 0xfb, 0xea, + 0x00, 0xf8, 0x00, 0xb5, 0x00, 0x04, 0x05, 0x3c, 0x00, 0xf8, 0xb4, 0x00, + 0xfd, 0xb5, 0x00, 0xfd, 0xbb, 0x00, 0xf8, 0xc5, 0x00, 0xf2, 0xea, 0x00, + 0xf8, 0x20, 0x24, 0x00, 0xf2, 0x44, 0x00, 0xf2, 0x48, 0x00, 0xf8, 0x4c, + 0x00, 0xf5, 0x52, 0x00, 0xfb, 0x55, 0x00, 0xf5, 0x58, 0x00, 0xf8, 0x5c, + 0x00, 0xf2, 0x62, 0x00, 0xf2, 0x69, 0x00, 0xf2, 0x6a, 0x00, 0xf2, 0x6b, + 0x00, 0xf2, 0x6c, 0x00, 0xf2, 0x6d, 0x00, 0xf2, 0x6e, 0x00, 0xf2, 0x70, + 0x00, 0xf8, 0x71, 0x00, 0xf8, 0x72, 0x00, 0xf8, 0x73, 0x00, 0xf8, 0x79, + 0x00, 0xfb, 0x7a, 0x00, 0xfb, 0x7b, 0x00, 0xfb, 0x7c, 0x00, 0xfb, 0x7d, + 0x00, 0xfb, 0x7e, 0x00, 0xf8, 0xad, 0x00, 0xf2, 0xae, 0x00, 0xf2, 0xb4, + 0x00, 0xfd, 0xba, 0x00, 0xf2, 0xc5, 0x00, 0xe2, 0xc7, 0x00, 0xf2, 0xc9, + 0x00, 0xf2, 0xeb, 0x00, 0xf2, 0x06, 0x37, 0x00, 0xfb, 0x3c, 0x00, 0xf9, + 0xb4, 0x00, 0xfd, 0xb5, 0x00, 0xfd, 0xbb, 0x00, 0xf9, 0xc5, 0x00, 0xfd, + 0xea, 0x00, 0xf9, 0x02, 0xb4, 0x00, 0xfb, 0xb5, 0x00, 0xfc, 0xc5, 0x00, + 0xfb, 0x02, 0xb4, 0x00, 0xfb, 0xb5, 0x00, 0xfc, 0xc5, 0x00, 0xf8, 0x26, + 0x26, 0x00, 0xf8, 0x32, 0x00, 0xf8, 0x37, 0x00, 0xf4, 0x38, 0x00, 0xfd, + 0x3a, 0x00, 0xfb, 0x3c, 0x00, 0xfb, 0x48, 0x00, 0xf9, 0x52, 0x00, 0xf9, + 0x58, 0x00, 0xf9, 0x5c, 0x00, 0xf5, 0x64, 0x00, 0xf8, 0x67, 0x00, 0xf8, + 0x68, 0x00, 0xfd, 0x70, 0x00, 0xf9, 0x71, 0x00, 0xf9, 0x72, 0x00, 0xf9, + 0x73, 0x00, 0xf9, 0x79, 0x00, 0xf9, 0x7a, 0x00, 0xf9, 0x7b, 0x00, 0xf9, + 0x7c, 0x00, 0xf9, 0x7d, 0x00, 0xf9, 0x7e, 0x00, 0xf9, 0xa9, 0x00, 0xf7, + 0xaf, 0x00, 0xf8, 0xb4, 0x00, 0xfc, 0xb5, 0x00, 0xfc, 0xba, 0x00, 0xf5, + 0xbb, 0x00, 0xfb, 0xd0, 0x00, 0xf8, 0xd1, 0x00, 0xf8, 0xd2, 0x00, 0xf8, + 0xd3, 0x00, 0xfd, 0xd4, 0x00, 0xfd, 0xd5, 0x00, 0xfd, 0xea, 0x00, 0xfb, + 0xeb, 0x00, 0xf5, 0xfb, 0x00, 0xf8, 0xfd, 0x00, 0xf8, 0x1b, 0x24, 0x00, + 0x05, 0x32, 0x00, 0xfb, 0x37, 0x00, 0xea, 0x38, 0x00, 0xf9, 0x39, 0x00, + 0xef, 0x3a, 0x00, 0xf2, 0x3c, 0x00, 0xea, 0x5c, 0x00, 0xf2, 0x62, 0x00, + 0x05, 0x67, 0x00, 0xfb, 0x68, 0x00, 0xf9, 0xad, 0x00, 0x05, 0xae, 0x00, + 0x05, 0xaf, 0x00, 0xfb, 0xb4, 0x00, 0xde, 0xb5, 0x00, 0xd4, 0xba, 0x00, + 0xf2, 0xbb, 0x00, 0xea, 0xc7, 0x00, 0x05, 0xc9, 0x00, 0x05, 0xd0, 0x00, + 0xfb, 0xd1, 0x00, 0xfb, 0xd2, 0x00, 0xfb, 0xd3, 0x00, 0xf9, 0xd4, 0x00, + 0xf9, 0xd5, 0x00, 0xf9, 0xea, 0x00, 0xea, 0xeb, 0x00, 0xf2, 0x05, 0x3b, + 0x00, 0xf7, 0x3c, 0x00, 0xf8, 0xb4, 0x00, 0xfd, 0xbb, 0x00, 0xf8, 0xc5, + 0x00, 0xf2, 0xea, 0x00, 0xf8, 0x1e, 0x24, 0x00, 0xf7, 0x3c, 0x00, 0xfd, + 0x44, 0x00, 0xfa, 0x48, 0x00, 0xfb, 0x4c, 0x00, 0xfd, 0x52, 0x00, 0xfb, + 0x62, 0x00, 0xf7, 0x69, 0x00, 0xfa, 0x6a, 0x00, 0xfa, 0x6b, 0x00, 0xfa, + 0x6c, 0x00, 0xfa, 0x6d, 0x00, 0xfa, 0x6e, 0x00, 0xfa, 0x70, 0x00, 0xfb, + 0x71, 0x00, 0xfb, 0x72, 0x00, 0xfb, 0x73, 0x00, 0xfb, 0x79, 0x00, 0xfb, + 0x7a, 0x00, 0xfb, 0x7b, 0x00, 0xfb, 0x7c, 0x00, 0xfb, 0x7d, 0x00, 0xfb, + 0xad, 0x00, 0xf7, 0xae, 0x00, 0xf7, 0xb4, 0x00, 0x04, 0xb5, 0x00, 0x04, + 0xbb, 0x00, 0xfd, 0xc5, 0x00, 0xe6, 0xc7, 0x00, 0xf7, 0xc9, 0x00, 0xf7, + 0xea, 0x00, 0xfd, 0x01, 0xb4, 0x00, 0xfd, 0xc5, 0x00, 0xf7, 0x29, 0x24, + 0x00, 0xfb, 0x26, 0x00, 0xf9, 0x37, 0x00, 0xf5, 0x39, 0x00, 0xf8, 0x3a, + 0x00, 0xfb, 0x3c, 0x00, 0xf7, 0x44, 0x00, 0xfd, 0x48, 0x00, 0xfa, 0x52, + 0x00, 0xfa, 0x58, 0x00, 0xfa, 0x5c, 0x00, 0xf8, 0x62, 0x00, 0xfb, 0x64, + 0x00, 0xf9, 0x69, 0x00, 0xfd, 0x6a, 0x00, 0xfd, 0x6b, 0x00, 0xfd, 0x6c, + 0x00, 0xfd, 0x6d, 0x00, 0xfd, 0x6e, 0x00, 0xfd, 0x70, 0x00, 0xfa, 0x71, + 0x00, 0xfa, 0x72, 0x00, 0xfa, 0x73, 0x00, 0xfa, 0x79, 0x00, 0xfa, 0x7a, + 0x00, 0xfa, 0x7b, 0x00, 0xfa, 0x7c, 0x00, 0xfa, 0x7d, 0x00, 0xfa, 0x7e, + 0x00, 0xfa, 0xa9, 0x00, 0xf8, 0xad, 0x00, 0xfb, 0xae, 0x00, 0xfb, 0xb4, + 0x00, 0xf5, 0xb5, 0x00, 0xf7, 0xba, 0x00, 0xf8, 0xbb, 0x00, 0xf7, 0xc7, + 0x00, 0xfb, 0xc9, 0x00, 0xfb, 0xea, 0x00, 0xf7, 0xeb, 0x00, 0xf8, 0xfb, + 0x00, 0xf9, 0xfd, 0x00, 0xf9, 0x05, 0x24, 0x00, 0x04, 0x62, 0x00, 0x04, + 0xad, 0x00, 0x04, 0xae, 0x00, 0x04, 0xc7, 0x00, 0x04, 0xc9, 0x00, 0x04, + 0x2e, 0x24, 0x00, 0xf4, 0x26, 0x00, 0xf7, 0x44, 0x00, 0xe5, 0x46, 0x00, + 0xe4, 0x48, 0x00, 0xe4, 0x4c, 0x00, 0xfc, 0x52, 0x00, 0xe4, 0x55, 0x00, + 0xe8, 0x56, 0x00, 0xe5, 0x58, 0x00, 0xe7, 0x5a, 0x00, 0xe5, 0x5c, 0x00, + 0xe6, 0x62, 0x00, 0xf4, 0x64, 0x00, 0xf7, 0x69, 0x00, 0xe5, 0x6a, 0x00, + 0xe5, 0x6b, 0x00, 0xe5, 0x6c, 0x00, 0xe5, 0x6d, 0x00, 0xe5, 0x6e, 0x00, + 0xe5, 0x6f, 0x00, 0xe4, 0x70, 0x00, 0xe4, 0x71, 0x00, 0xe4, 0x72, 0x00, + 0xe4, 0x73, 0x00, 0xe4, 0x79, 0x00, 0xe4, 0x7a, 0x00, 0xe4, 0x7b, 0x00, + 0xe4, 0x7c, 0x00, 0xe4, 0x7d, 0x00, 0xe4, 0x7e, 0x00, 0xe7, 0xa9, 0x00, + 0xf2, 0xaa, 0x00, 0xf8, 0xad, 0x00, 0xf4, 0xae, 0x00, 0xf4, 0xb5, 0x00, + 0xfd, 0xba, 0x00, 0xe6, 0xc5, 0x00, 0xeb, 0xc7, 0x00, 0xf4, 0xc9, 0x00, + 0xf4, 0xe4, 0x00, 0xe5, 0xeb, 0x00, 0xe6, 0xfa, 0x00, 0xe5, 0xfb, 0x00, + 0xf7, 0xfc, 0x00, 0xe4, 0xfd, 0x00, 0xf7, 0xfe, 0x00, 0xe4, 0x20, 0x24, + 0x00, 0xf7, 0x44, 0x00, 0xf4, 0x48, 0x00, 0xf4, 0x4c, 0x00, 0xfd, 0x52, + 0x00, 0xf4, 0x58, 0x00, 0xf6, 0x5c, 0x00, 0xfd, 0x62, 0x00, 0xf7, 0x69, + 0x00, 0xf4, 0x6a, 0x00, 0xf4, 0x6b, 0x00, 0xf4, 0x6c, 0x00, 0xf4, 0x6d, + 0x00, 0xf4, 0x6e, 0x00, 0xf4, 0x70, 0x00, 0xf4, 0x71, 0x00, 0xf4, 0x72, + 0x00, 0xf4, 0x73, 0x00, 0xf4, 0x79, 0x00, 0xf4, 0x7a, 0x00, 0xf4, 0x7b, + 0x00, 0xf4, 0x7c, 0x00, 0xf4, 0x7d, 0x00, 0xf4, 0x7e, 0x00, 0xf6, 0xa9, + 0x00, 0xf3, 0xaa, 0x00, 0xf8, 0xad, 0x00, 0xf7, 0xae, 0x00, 0xf7, 0xba, + 0x00, 0xfd, 0xc5, 0x00, 0xea, 0xc7, 0x00, 0xf7, 0xc9, 0x00, 0xf7, 0xeb, + 0x00, 0xfd, 0x1d, 0x24, 0x00, 0xf8, 0x44, 0x00, 0xf7, 0x48, 0x00, 0xf7, + 0x4c, 0x00, 0xfd, 0x52, 0x00, 0xf7, 0x55, 0x00, 0xfa, 0x58, 0x00, 0xfb, + 0x62, 0x00, 0xf8, 0x69, 0x00, 0xf7, 0x6a, 0x00, 0xf7, 0x6b, 0x00, 0xf7, + 0x6c, 0x00, 0xf7, 0x6d, 0x00, 0xf7, 0x6e, 0x00, 0xf7, 0x70, 0x00, 0xf7, + 0x71, 0x00, 0xf7, 0x72, 0x00, 0xf7, 0x73, 0x00, 0xf7, 0x79, 0x00, 0xf7, + 0x7a, 0x00, 0xf7, 0x7b, 0x00, 0xf7, 0x7c, 0x00, 0xf7, 0x7d, 0x00, 0xf7, + 0x7e, 0x00, 0xfb, 0xa9, 0x00, 0xf8, 0xad, 0x00, 0xf8, 0xae, 0x00, 0xf8, + 0xc5, 0x00, 0xeb, 0xc7, 0x00, 0xf8, 0xc9, 0x00, 0xf8, 0x12, 0x26, 0x00, + 0xf5, 0x32, 0x00, 0xf7, 0x48, 0x00, 0xfa, 0x64, 0x00, 0xf5, 0x67, 0x00, + 0xf7, 0x70, 0x00, 0xfa, 0x71, 0x00, 0xfa, 0x72, 0x00, 0xfa, 0x73, 0x00, + 0xfa, 0xa9, 0x00, 0xf8, 0xaf, 0x00, 0xf7, 0xb4, 0x00, 0xf4, 0xb5, 0x00, + 0xfb, 0xc5, 0x00, 0xfd, 0xd0, 0x00, 0xf7, 0xd1, 0x00, 0xf7, 0xd2, 0x00, + 0xf7, 0xfb, 0x00, 0xf5, 0xfd, 0x00, 0xf5, 0x28, 0x24, 0x00, 0xf4, 0x26, + 0x00, 0xf8, 0x32, 0x00, 0xf8, 0x44, 0x00, 0xea, 0x48, 0x00, 0xea, 0x4c, + 0x00, 0xfb, 0x52, 0x00, 0xea, 0x58, 0x00, 0xee, 0x62, 0x00, 0xf4, 0x64, + 0x00, 0xf8, 0x67, 0x00, 0xf8, 0x69, 0x00, 0xea, 0x6a, 0x00, 0xea, 0x6b, + 0x00, 0xea, 0x6c, 0x00, 0xea, 0x6d, 0x00, 0xea, 0x6e, 0x00, 0xea, 0x70, + 0x00, 0xea, 0x71, 0x00, 0xea, 0x72, 0x00, 0xea, 0x73, 0x00, 0xea, 0x79, + 0x00, 0xea, 0x7a, 0x00, 0xea, 0x7b, 0x00, 0xea, 0x7c, 0x00, 0xea, 0x7d, + 0x00, 0xea, 0x7e, 0x00, 0xee, 0xa9, 0x00, 0xef, 0xaa, 0x00, 0xf5, 0xad, + 0x00, 0xf4, 0xae, 0x00, 0xf4, 0xaf, 0x00, 0xf8, 0xb4, 0x00, 0xf8, 0xc5, + 0x00, 0xeb, 0xc7, 0x00, 0xf4, 0xc9, 0x00, 0xf4, 0xd0, 0x00, 0xf8, 0xd1, + 0x00, 0xf8, 0xd2, 0x00, 0xf8, 0xfb, 0x00, 0xf8, 0xfd, 0x00, 0xf8, 0x02, + 0xa9, 0x00, 0xfb, 0xb4, 0x00, 0x06, 0xc5, 0x00, 0xee, 0x0f, 0x48, 0x00, + 0xfb, 0x52, 0x00, 0xfb, 0x58, 0x00, 0xfc, 0x5c, 0x00, 0xfb, 0x70, 0x00, + 0xfb, 0x71, 0x00, 0xfb, 0x72, 0x00, 0xfb, 0x73, 0x00, 0xfb, 0x79, 0x00, + 0xfb, 0x7a, 0x00, 0xfb, 0x7b, 0x00, 0xfb, 0x7c, 0x00, 0xfb, 0x7d, 0x00, + 0xfb, 0x7e, 0x00, 0xfc, 0xba, 0x00, 0xfb, 0xeb, 0x00, 0xfb, 0x02, 0xb4, + 0x00, 0xf5, 0xb5, 0x00, 0xf8, 0xc5, 0x00, 0xfa, 0x03, 0x5b, 0x00, 0xfc, + 0xb4, 0x00, 0xf5, 0xb5, 0x00, 0xfb, 0xc5, 0x00, 0xf7, 0x12, 0x46, 0x00, + 0xfd, 0x48, 0x00, 0xfd, 0x52, 0x00, 0xfd, 0x5b, 0x00, 0xfd, 0x6f, 0x00, + 0xfd, 0x70, 0x00, 0xfd, 0x71, 0x00, 0xfd, 0x72, 0x00, 0xfd, 0x73, 0x00, + 0xfd, 0x79, 0x00, 0xfd, 0x7a, 0x00, 0xfd, 0x7b, 0x00, 0xfd, 0x7c, 0x00, + 0xfd, 0x7d, 0x00, 0xfd, 0xa9, 0x00, 0xfb, 0xb5, 0x00, 0x08, 0xc5, 0x00, + 0xe7, 0xfc, 0x00, 0xfd, 0xfe, 0x00, 0xfd, 0x00, 0xc5, 0x00, 0xea, 0x00, + 0xc5, 0x00, 0xef, 0x0a, 0x48, 0x00, 0xfc, 0x52, 0x00, 0xfc, 0x70, 0x00, + 0xfc, 0x71, 0x00, 0xfc, 0x72, 0x00, 0xfc, 0x73, 0x00, 0xfc, 0x79, 0x00, + 0xfc, 0x7a, 0x00, 0xfc, 0x7b, 0x00, 0xfc, 0x7c, 0x00, 0xfc, 0x7d, 0x00, + 0xfc, 0x00, 0xc5, 0x00, 0xe8, 0x04, 0x37, 0x00, 0xf8, 0x39, 0x00, 0xf8, + 0x3c, 0x00, 0xf5, 0xbb, 0x00, 0xf5, 0xea, 0x00, 0xf5, 0x0d, 0x24, 0x00, + 0xfb, 0x25, 0x00, 0xfb, 0x37, 0x00, 0xf2, 0x39, 0x00, 0xf3, 0x3a, 0x00, + 0xf8, 0x3b, 0x00, 0xf8, 0x3c, 0x00, 0xef, 0x62, 0x00, 0xfb, 0xad, 0x00, + 0xfb, 0xae, 0x00, 0xfb, 0xbb, 0x00, 0xef, 0xc7, 0x00, 0xfb, 0xc9, 0x00, + 0xfb, 0xea, 0x00, 0xef, 0x2d, 0x24, 0x00, 0xeb, 0x25, 0x00, 0xfc, 0x26, + 0x00, 0xfb, 0x27, 0x00, 0xfc, 0x29, 0x00, 0xfc, 0x2a, 0x00, 0xfb, 0x2b, + 0x00, 0xfc, 0x2d, 0x00, 0xfc, 0x2e, 0x00, 0xfc, 0x2f, 0x00, 0xfc, 0x32, + 0x00, 0xfb, 0x33, 0x00, 0xfc, 0x34, 0x00, 0xfb, 0x35, 0x00, 0xfc, 0x3b, + 0x00, 0xf7, 0x49, 0x00, 0xfb, 0x51, 0x00, 0xf8, 0x52, 0x00, 0xf5, 0x55, + 0x00, 0xf8, 0x59, 0x00, 0xfb, 0x5a, 0x00, 0xfb, 0x5c, 0x00, 0xfb, 0x62, + 0x00, 0xeb, 0x64, 0x00, 0xfb, 0x67, 0x00, 0xfb, 0x78, 0x00, 0xf8, 0x79, + 0x00, 0xf5, 0x7a, 0x00, 0xf5, 0x7b, 0x00, 0xf5, 0x7c, 0x00, 0xf5, 0x7d, + 0x00, 0xf5, 0xad, 0x00, 0xeb, 0xae, 0x00, 0xeb, 0xaf, 0x00, 0xfb, 0xba, + 0x00, 0xfb, 0xc7, 0x00, 0xeb, 0xc9, 0x00, 0xeb, 0xd0, 0x00, 0xfb, 0xd1, + 0x00, 0xfb, 0xd2, 0x00, 0xfb, 0xe9, 0x00, 0xfb, 0xeb, 0x00, 0xfb, 0xec, + 0x00, 0xfc, 0xf6, 0x00, 0xfb, 0xfb, 0x00, 0xfb, 0xfd, 0x00, 0xfb, 0x32, + 0x24, 0x00, 0x04, 0x25, 0x00, 0xfb, 0x26, 0x00, 0xf8, 0x27, 0x00, 0xfb, + 0x29, 0x00, 0xfb, 0x2a, 0x00, 0xfb, 0x2b, 0x00, 0xfb, 0x2d, 0x00, 0x05, + 0x2e, 0x00, 0xfb, 0x2f, 0x00, 0xfb, 0x32, 0x00, 0xf8, 0x33, 0x00, 0xfb, + 0x34, 0x00, 0xf8, 0x35, 0x00, 0xfb, 0x37, 0x00, 0xea, 0x39, 0x00, 0xe2, + 0x3a, 0x00, 0xec, 0x3b, 0x00, 0xfb, 0x3c, 0x00, 0xe2, 0x51, 0x00, 0xfb, + 0x52, 0x00, 0xfb, 0x55, 0x00, 0xfb, 0x59, 0x00, 0xee, 0x5a, 0x00, 0xf1, + 0x5c, 0x00, 0xf8, 0x62, 0x00, 0x04, 0x64, 0x00, 0xf8, 0x67, 0x00, 0xf8, + 0x78, 0x00, 0xfb, 0x79, 0x00, 0xfb, 0x7a, 0x00, 0xfb, 0x7b, 0x00, 0xfb, + 0x7c, 0x00, 0xfb, 0x7d, 0x00, 0xfb, 0xad, 0x00, 0x04, 0xae, 0x00, 0x04, + 0xaf, 0x00, 0xf8, 0xba, 0x00, 0xf8, 0xbb, 0x00, 0xe2, 0xc7, 0x00, 0x04, + 0xc9, 0x00, 0x04, 0xd0, 0x00, 0xf8, 0xd1, 0x00, 0xf8, 0xd2, 0x00, 0xf8, + 0xe9, 0x00, 0xfb, 0xea, 0x00, 0xe2, 0xeb, 0x00, 0xf8, 0xec, 0x00, 0xfb, + 0xf6, 0x00, 0xfb, 0xfb, 0x00, 0xf8, 0xfd, 0x00, 0xf8, 0x02, 0xb4, 0x00, + 0xfa, 0xb5, 0x00, 0xf8, 0xc5, 0x00, 0xfb, 0x00, 0xc5, 0x00, 0xf2, 0x4e, + 0x46, 0x53, 0x53}; +} +#endif diff --git a/src/MACHDYN/fix_smd_wall_surface.cpp b/src/MACHDYN/fix_smd_wall_surface.cpp index 8510f722cb2..04851d1c740 100644 --- a/src/MACHDYN/fix_smd_wall_surface.cpp +++ b/src/MACHDYN/fix_smd_wall_surface.cpp @@ -21,8 +21,8 @@ #include "atom_vec.h" #include "comm.h" #include "domain.h" -#include "dump_image.h" #include "error.h" +#include "graphics.h" #include "memory.h" #include "text_file_reader.h" @@ -359,7 +359,7 @@ int FixSMDWallSurface::image(int *&objs, double **&parms) numobjs = 0; for (int i = 0; i < nlocal; ++i) { if (type[i] == wall_particle_type) { - imgobjs[numobjs] = DumpImage::TRI; + imgobjs[numobjs] = Graphics::TRI; imgparms[numobjs][0] = wall_particle_type; imgparms[numobjs][1] = verts[i][0]; imgparms[numobjs][2] = verts[i][1]; diff --git a/src/MISC/fix_imd.cpp b/src/MISC/fix_imd.cpp index 61ea2cc61f3..090ac680e67 100644 --- a/src/MISC/fix_imd.cpp +++ b/src/MISC/fix_imd.cpp @@ -809,7 +809,8 @@ void FixIMD::setup_v2() { taginthash_init(hashtable, num_coords); idmap = (void *)hashtable; - int tmp, ndata; + int tmp = 0; + int ndata = 0; auto *buf = static_cast(coord_data); if (me == 0) { @@ -911,8 +912,8 @@ void FixIMD::setup_v3() taginthash_init(hashtable, num_coords); idmap = (void *)hashtable; - int tmp, ndata; - + int tmp = 0; + int ndata = 0; struct commdata *buf = nullptr; if (imdsinfo->coords) { buf = static_cast(coord_data); @@ -1236,7 +1237,8 @@ void FixIMD::handle_step_v2() { coord_data = memory->smalloc(maxbuf,"imd:coord_data"); } - int tmp, ndata; + int tmp = 0; + int ndata = 0; buf = static_cast(coord_data); if (me == 0) { diff --git a/src/Makefile b/src/Makefile index 75cd0867dd8..e822243c9e3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -78,6 +78,7 @@ PACKAGE = \ extra-molecule \ extra-pair \ fep \ + graphics \ granular \ intel \ interlayer \ @@ -119,7 +120,7 @@ PACKAGE = \ # NOTE: the last four packages must remain at the end since # they depend on other packages to be installed first. -PACKBASIC = molecule kspace manybody rigid +PACKBASIC = molecule kspace manybody rigid graphics PACKMOST = \ kspace \ @@ -150,6 +151,7 @@ PACKMOST = \ extra-molecule \ extra-pair \ fep \ + graphics \ granular \ interlayer \ manybody \ diff --git a/src/Purge.list b/src/Purge.list index bad4d7e6aee..b4ce98673e4 100644 --- a/src/Purge.list +++ b/src/Purge.list @@ -53,6 +53,33 @@ lmpinstalledpkgs.h lmpgitversion.h mliap_model_python_couple.cpp mliap_model_python_couple.h +mliap_model_python_couple_kokkos.cpp +mliap_model_python_couple_kokkos.h +mliap_unified_couple.cpp +mliap_unified_couple.h +mliap_unified_couple_kokkos.cpp +mliap_unified_couple_kokkos.h +# moved to new GRAPHICS package in Jan 2026 +dump_image.cpp +dump_image.h +dump_movie.cpp +dump_movie.h +fix_graphics_arrows.cpp +fix_graphics_arrows.h +fix_graphics_isosurface.cpp +fix_graphics_isosurface.h +fix_graphics_labels.cpp +fix_graphics_labels.h +fix_graphics_objects.cpp +fix_graphics_objects.h +fix_graphics_periodic.cpp +fix_graphics_periodic.h +image.cpp +image.h +image_objects.cpp +image_objects.h +scalable_font.cpp +scalable_font.h # renamed in Nov 2025 fix_addtorque.h fix_addtorque.cpp diff --git a/src/REAXFF/fix_reaxff_bonds.cpp b/src/REAXFF/fix_reaxff_bonds.cpp index f2b1412f48a..b03fbe7ed8e 100644 --- a/src/REAXFF/fix_reaxff_bonds.cpp +++ b/src/REAXFF/fix_reaxff_bonds.cpp @@ -21,8 +21,8 @@ #include "atom.h" #include "comm.h" #include "error.h" -#include "dump_image.h" #include "force.h" +#include "graphics.h" #include "memory.h" #include "neigh_list.h" #include "update.h" @@ -52,6 +52,7 @@ FixReaxFFBonds::FixReaxFFBonds(LAMMPS *lmp, int narg, char **arg) : padflag = 0; first_flag = true; numobjs = 0; + dynamic_group_allow = 1; // applies only to FixReaxFFBonds::image() nevery = utils::inumeric(FLERR,arg[3],false,lmp); if (nevery <= 0) error->all(FLERR, 3, "Illegal fix reaxff/bonds nevery value {}", nevery); @@ -385,8 +386,8 @@ int FixReaxFFBonds::image(int *&objs, double **&parms) memory->create(imgobjs, numobjs, "reaxff/bonds:imgobjs"); memory->create(imgparms, numobjs, 8, "reaxff/bonds:imgparms"); - const int nlocal = atom->nlocal; const int *type = atom->type; + const int *mask = atom->mask; const double * const * const x = atom->x; int inum = reaxff->list->inum; @@ -395,20 +396,24 @@ int FixReaxFFBonds::image(int *&objs, double **&parms) int n = 0; for (int ii = 0; ii < inum; ++ii) { int i = ilist[ii]; - for (int jj = 0; jj < numneigh[i]; ++jj) { - int j = atom->map(neighid[i][jj]); - j = domain->closest_image(i,j); - if (j < 0) continue; - imgobjs[n] = DumpImage::BOND; - imgparms[n][0] = type[i]; - imgparms[n][1] = type[j]; - imgparms[n][2] = x[i][0]; - imgparms[n][3] = x[i][1]; - imgparms[n][4] = x[i][2]; - imgparms[n][5] = x[j][0]; - imgparms[n][6] = x[j][1]; - imgparms[n][7] = x[j][2]; - ++n; + if (mask[i] & groupbit) { + for (int jj = 0; jj < numneigh[i]; ++jj) { + int j = atom->map(neighid[i][jj]); + j = domain->closest_image(i,j); + if (j < 0) continue; + if (mask[j] & groupbit) { + imgobjs[n] = Graphics::BOND; + imgparms[n][0] = type[i]; + imgparms[n][1] = type[j]; + imgparms[n][2] = x[i][0]; + imgparms[n][3] = x[i][1]; + imgparms[n][4] = x[i][2]; + imgparms[n][5] = x[j][0]; + imgparms[n][6] = x[j][1]; + imgparms[n][7] = x[j][2]; + ++n; + } + } } } diff --git a/src/RIGID/fix_rigid_small.cpp b/src/RIGID/fix_rigid_small.cpp index 106d10f281a..441d7fb674c 100644 --- a/src/RIGID/fix_rigid_small.cpp +++ b/src/RIGID/fix_rigid_small.cpp @@ -2647,7 +2647,8 @@ void FixRigidSmall::write_restart_file(const char *file) // proc 0 pings each proc, receives its chunk, writes to file // all other procs wait for ping, send their chunk to proc 0 - int tmp,recvrow; + int tmp = 0; + int recvrow = 0; if (comm->me == 0) { MPI_Status status; diff --git a/src/SRD/fix_wall_srd.cpp b/src/SRD/fix_wall_srd.cpp index d9e55a49f2e..3392168285d 100644 --- a/src/SRD/fix_wall_srd.cpp +++ b/src/SRD/fix_wall_srd.cpp @@ -17,6 +17,8 @@ #include "domain.h" #include "error.h" #include "fix.h" +#include "fix_wall.h" +#include "graphics.h" #include "input.h" #include "lattice.h" #include "memory.h" @@ -34,9 +36,10 @@ enum { NONE, EDGE, CONSTANT, VARIABLE }; /* ---------------------------------------------------------------------- */ FixWallSRD::FixWallSRD(LAMMPS *lmp, int narg, char **arg) : - Fix(lmp, narg, arg), nwall(0), fwall(nullptr), fwall_all(nullptr) + Fix(lmp, narg, arg), nwall(0), fwall(nullptr), fwall_all(nullptr), + imgobjs(nullptr), imgparms(nullptr) { - if (narg < 4) error->all(FLERR, "Illegal fix wall/srd command"); + if (narg < 4) utils::missing_cmd_args(FLERR, "fix wall/srd", error); // parse args @@ -48,7 +51,7 @@ FixWallSRD::FixWallSRD(LAMMPS *lmp, int narg, char **arg) : const std::string thisarg(arg[iarg]); if ((thisarg == "xlo") || (thisarg == "ylo") || (thisarg == "zlo") || (thisarg == "xhi") || (thisarg == "yhi") || (thisarg == "zhi")) { - if (iarg+2 > narg) error->all(FLERR, "Illegal fix wall/srd command"); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix wall/srd " + thisarg, error); int newwall; if (thisarg == "xlo") newwall = XLO; @@ -60,7 +63,7 @@ FixWallSRD::FixWallSRD(LAMMPS *lmp, int narg, char **arg) : for (int m = 0; (m < nwall) && (m < 6); m++) if (newwall == wallwhich[m]) - error->all(FLERR, "Wall defined twice in fix wall/srd command"); + error->all(FLERR, iarg, "Wall {} defined twice in fix wall/srd command", thisarg); wallwhich[nwall] = newwall; if (strcmp(arg[iarg+1], "EDGE") == 0) { @@ -83,16 +86,16 @@ FixWallSRD::FixWallSRD(LAMMPS *lmp, int narg, char **arg) : iarg += 2; } else if (thisarg == "units") { - if (iarg+2 > narg) error->all(FLERR, "Illegal wall/srd command"); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix wall/srd units", error); if (strcmp(arg[iarg+1], "box") == 0) scaleflag = 0; else if (strcmp(arg[iarg+1], "lattice") == 0) scaleflag = 1; else - error->all(FLERR, "Illegal fix wall/srd command"); + error->all(FLERR, iarg+1, "Unknown fix wall/srd units setting {}", arg[iarg+1]); iarg += 2; } else - error->all(FLERR, "Illegal fix wall/srd command"); + error->all(FLERR, iarg, "Unknown fix wall/srd keyword {}", arg[iarg]); } // error check @@ -101,11 +104,11 @@ FixWallSRD::FixWallSRD(LAMMPS *lmp, int narg, char **arg) : for (int m = 0; m < nwall; m++) { if ((wallwhich[m] == XLO || wallwhich[m] == XHI) && domain->xperiodic) - error->all(FLERR, "Cannot use fix wall/srd in periodic dimension"); + error->all(FLERR, "Cannot use fix wall/srd xlo or xhi with periodic x dimension"); if ((wallwhich[m] == YLO || wallwhich[m] == YHI) && domain->yperiodic) - error->all(FLERR, "Cannot use fix wall/srd in periodic dimension"); + error->all(FLERR, "Cannot use fix wall/srd ylo or yhi with periodic y dimension"); if ((wallwhich[m] == ZLO || wallwhich[m] == ZHI) && domain->zperiodic) - error->all(FLERR, "Cannot use fix wall/srd in periodic dimension"); + error->all(FLERR, "Cannot use fix wall/srd zlo or zhi with periodic z dimension"); } for (int m = 0; m < nwall; m++) @@ -166,6 +169,27 @@ FixWallSRD::FixWallSRD(LAMMPS *lmp, int narg, char **arg) : for (int m = 0; m < nwall; m++) if (wallstyle[m] == VARIABLE) varflag = 1; laststep = -1; + + // for rendering walls with dump image. + if (domain->dimension == 2) { + // one cylinder object per wall to draw in 2d + memory->create(imgobjs, nwall, "fix_wall:imgobjs"); + memory->create(imgparms, nwall, 8, "fix_wall:imgparms"); + for (int m = 0; m < nwall; ++m) { + imgobjs[m] = Graphics::CYLINDER; + imgparms[m][0] = 1; // use color of first atom type by default + } + } else { + // two triangle objects per wall to draw in 3d + memory->create(imgobjs, 2 * nwall, "fix_wall:imgobjs"); + memory->create(imgparms, 2 * nwall, 10, "fix_wall:imgparms"); + for (int m = 0; m < nwall; ++m) { + imgobjs[2 * m] = Graphics::TRIANGLE; + imgobjs[2 * m + 1] = Graphics::TRIANGLE; + imgparms[2 * m][0] = 1; // use color of first atom type by default + imgparms[2 * m + 1][0] = 1; // use color of first atom type by default + } + } } /* ---------------------------------------------------------------------- */ @@ -176,6 +200,9 @@ FixWallSRD::~FixWallSRD() if (wallstyle[m] == VARIABLE) delete[] varstr[m]; memory->destroy(fwall); memory->destroy(fwall_all); + + memory->destroy(imgobjs); + memory->destroy(imgparms); } /* ---------------------------------------------------------------------- */ @@ -190,17 +217,18 @@ int FixWallSRD::setmask() void FixWallSRD::init() { - int flag = 0; - for (int m = 0; m < modify->nfix; m++) - if (utils::strmatch(modify->fix[m]->style, "^srd")) flag = 1; - if (!flag) error->all(FLERR, "Cannot use fix wall/srd without fix srd"); + if (modify->get_fix_by_style("^srd").size() == 0) + error->all(FLERR, Error::NOLASTLINE, "Cannot use fix wall/srd without fix srd"); for (int m = 0; m < nwall; m++) { if (wallstyle[m] != VARIABLE) continue; varindex[m] = input->variable->find(varstr[m]); - if (varindex[m] < 0) error->all(FLERR, "Variable name for fix wall/srd does not exist"); + if (varindex[m] < 0) + error->all(FLERR, Error::NOLASTLINE, + "Variable {} for fix wall/srd does not exist", varstr[m]); if (!input->variable->equalstyle(varindex[m])) - error->all(FLERR, "Variable for fix wall/srd is invalid style"); + error->all(FLERR, Error::NOLASTLINE, + "Variable {} for fix wall/srd is invalid style", varstr[m]); } dt = update->dt; @@ -251,6 +279,8 @@ void FixWallSRD::wall_params(int flag) } fwall[m][0] = fwall[m][1] = fwall[m][2] = 0.0; + + FixWall::update_image_plane(m, wallwhich[m], xnew, imgparms, domain); } laststep = ntimestep; @@ -262,3 +292,19 @@ void FixWallSRD::wall_params(int flag) force_flag = 0; } + +/* ---------------------------------------------------------------------- + provide graphics information to dump image to render wall as plane + data has been copied to dedicated storage during fix indent execution +------------------------------------------------------------------------- */ + +int FixWallSRD::image(int *&objs, double **&parms) +{ + objs = imgobjs; + parms = imgparms; + if (domain->dimension == 2) { + return nwall; + } else { + return 2 * nwall; + } +} diff --git a/src/SRD/fix_wall_srd.h b/src/SRD/fix_wall_srd.h index 3a96a73be49..9f6cdbd1953 100644 --- a/src/SRD/fix_wall_srd.h +++ b/src/SRD/fix_wall_srd.h @@ -37,6 +37,8 @@ class FixWallSRD : public Fix { void init() override; double compute_array(int, int) override; + int image(int *&, double **&) override; + void wall_params(int); private: @@ -51,9 +53,10 @@ class FixWallSRD : public Fix { double **fwall_all; int force_flag; -}; + int *imgobjs; + double **imgparms; +}; } // namespace LAMMPS_NS - #endif #endif diff --git a/src/compute_com_chunk.cpp b/src/compute_com_chunk.cpp index 582bd4f6fe7..feac8c0d385 100644 --- a/src/compute_com_chunk.cpp +++ b/src/compute_com_chunk.cpp @@ -19,6 +19,8 @@ #include "error.h" #include "memory.h" +#include + using namespace LAMMPS_NS; enum { ONCE, NFREQ, EVERY }; @@ -29,7 +31,7 @@ ComputeCOMChunk::ComputeCOMChunk(LAMMPS *lmp, int narg, char **arg) : ComputeChunk(lmp, narg, arg), masstotal(nullptr), massproc(nullptr), com(nullptr), comall(nullptr) { - if (narg != 4) error->all(FLERR, "Illegal compute com/chunk command"); + if (narg < 4) utils::missing_cmd_args(FLERR, "compute com/chunk", error); array_flag = 1; size_array_cols = 3; @@ -39,6 +41,20 @@ ComputeCOMChunk::ComputeCOMChunk(LAMMPS *lmp, int narg, char **arg) : ComputeCOMChunk::init(); ComputeCOMChunk::allocate(); + + // parse any remaining optional arguments + + wrapflag = false; + int iarg = 4; + while (iarg < narg) { + if (strcmp(arg[iarg], "wrap") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "compute com/chunk wrap", error); + wrapflag = utils::logical(FLERR, arg[iarg + 1], false, lmp); + iarg += 2; + } else { + error->all(FLERR, iarg, "Unknown compute com/chunk keyword {}", arg[iarg]); + } + } } /* ---------------------------------------------------------------------- */ @@ -114,6 +130,7 @@ void ComputeCOMChunk::compute_array() comall[i][0] /= masstotal[i]; comall[i][1] /= masstotal[i]; comall[i][2] /= masstotal[i]; + if (wrapflag) domain->remap(comall[i]); } else comall[i][0] = comall[i][1] = comall[i][2] = 0.0; } diff --git a/src/compute_com_chunk.h b/src/compute_com_chunk.h index 17731eb4b54..36e4436f01f 100644 --- a/src/compute_com_chunk.h +++ b/src/compute_com_chunk.h @@ -37,6 +37,7 @@ class ComputeCOMChunk : public ComputeChunk { private: double *massproc; double **com, **comall; + bool wrapflag; void allocate() override; }; diff --git a/src/dump.cpp b/src/dump.cpp index 058fa002461..f49e22ffc8f 100644 --- a/src/dump.cpp +++ b/src/dump.cpp @@ -110,6 +110,7 @@ Dump::Dump(LAMMPS *lmp, int /*narg*/, char **arg) : compressed = 0; binary = 0; multifile = 0; + multifile_override = 0; size_one = 0; multiproc = 0; @@ -476,7 +477,8 @@ void Dump::write() // ping each proc in my cluster, receive its data, write data to file // else wait for ping from fileproc, send my data to fileproc - int tmp,nlines,nchars; + int tmp = 0; + int nlines,nchars; MPI_Status status; MPI_Request request; diff --git a/src/dump.h b/src/dump.h index e5711af7a77..03d7c01ad9b 100644 --- a/src/dump.h +++ b/src/dump.h @@ -23,6 +23,7 @@ class Compute; class Dump : protected Pointers { friend class Output; + friend class WriteDump; public: char *id; // user-defined name of Dump @@ -52,6 +53,8 @@ class Dump : protected Pointers { virtual void unpack_reverse_comm(int, int *, double *) {} void modify_params(int, char **); + virtual void *extract(const char *, int &) { return nullptr; } + virtual double memory_usage(); protected: @@ -60,6 +63,7 @@ class Dump : protected Pointers { int compressed; // 1 if dump file is written compressed, 0 no int binary; // 1 if dump file is written binary, 0 no int multifile; // 0 = one big file, 1 = one file per timestep + int multifile_override; // 1 to override the "must have '*'" restriction in `write_dump` int multiproc; // 0 = proc 0 writes for all, // else # of procs writing files int nclusterprocs; // # of procs in my cluster that write to one file diff --git a/src/dump_cfg.h b/src/dump_cfg.h index 0047b9a6d9d..112d50361cf 100644 --- a/src/dump_cfg.h +++ b/src/dump_cfg.h @@ -26,8 +26,6 @@ namespace LAMMPS_NS { class DumpCFG : public DumpCustom { public: - int multifile_override; // used by write_dump command - DumpCFG(class LAMMPS *, int, char **); ~DumpCFG() override; diff --git a/src/dump_custom.cpp b/src/dump_custom.cpp index ff99233c3cf..a19303168f9 100644 --- a/src/dump_custom.cpp +++ b/src/dump_custom.cpp @@ -1899,7 +1899,7 @@ int DumpCustom::modify_param(int narg, char **arg) while (input && input->arg[argoff] && (strcmp(input->arg[argoff], arg[0]) != 0)) argoff++; if (strcmp(arg[0],"region") == 0) { - if (narg < 2) utils::missing_cmd_args(FLERR, "dump_modify", error); + if (narg < 2) utils::missing_cmd_args(FLERR, "dump_modify region", error); if (strcmp(arg[1],"none") == 0) { delete[] idregion; idregion = nullptr; diff --git a/src/fix_graphics.cpp b/src/fix_graphics.cpp deleted file mode 100644 index a58abae62f8..00000000000 --- a/src/fix_graphics.cpp +++ /dev/null @@ -1,680 +0,0 @@ -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, Sandia National Laboratories - LAMMPS development team: developers@lammps.org - - Copyright (2003) Sandia Corporation. Under the terms of Contract - DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains - certain rights in this software. This software is distributed under - the GNU General Public License. - - See the README file in the top-level LAMMPS directory. -------------------------------------------------------------------------- */ - -#include "fix_graphics.h" - -#include "comm.h" -#include "domain.h" -#include "dump_image.h" -#include "error.h" -#include "input.h" -#include "lattice.h" -#include "math_extra.h" -#include "memory.h" -#include "modify.h" -#include "update.h" -#include "variable.h" - -#include -#include - -using namespace LAMMPS_NS; -using namespace FixConst; - -enum { SPHERE, CYLINDER, ARROW, PROGBAR }; -enum { X = 0, Y, Z }; - -/* ---------------------------------------------------------------------- */ - -FixGraphics::FixGraphics(LAMMPS *lmp, int narg, char **arg) : - Fix(lmp, narg, arg), imgobjs(nullptr), imgparms(nullptr) -{ - if (narg < 4) utils::missing_cmd_args(FLERR, "fix graphics", error); - - // parse mandatory arg - - nevery = utils::inumeric(FLERR, arg[3], false, lmp); - if (nevery <= 0) error->all(FLERR, 3, "Illegal fix graphics nevery value {}", nevery); - global_freq = nevery; - dynamic_group_allow = 1; - - numobjs = 0; - varflag = 0; - - int iarg = 4; - while (iarg < narg) { - if (strcmp(arg[iarg], "sphere") == 0) { - if (iarg + 6 > narg) utils::missing_cmd_args(FLERR, "fix graphics sphere", error); - // clang-format off - SphereItem sphere{SPHERE, 1, {0.0, 0.0, 0.0}, 0.0, nullptr, nullptr, nullptr, nullptr, - -1, -1, -1, -1}; - // clang-format on - sphere.type = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); - if (strstr(arg[iarg + 2], "v_") == arg[iarg + 2]) { - varflag = 1; - sphere.xstr = utils::strdup(arg[iarg + 2] + 2); - } else - sphere.pos[X] = utils::numeric(FLERR, arg[iarg + 2], false, lmp); - if (strstr(arg[iarg + 3], "v_") == arg[iarg + 3]) { - varflag = 1; - sphere.ystr = utils::strdup(arg[iarg + 3] + 2); - } else - sphere.pos[Y] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); - if (strstr(arg[iarg + 4], "v_") == arg[iarg + 4]) { - varflag = 1; - sphere.zstr = utils::strdup(arg[iarg + 4] + 2); - } else - sphere.pos[Z] = utils::numeric(FLERR, arg[iarg + 4], false, lmp); - if (strstr(arg[iarg + 5], "v_") == arg[iarg + 5]) { - varflag = 1; - sphere.dstr = utils::strdup(arg[iarg + 5] + 2); - } else - sphere.diameter = 2.0 * utils::numeric(FLERR, arg[iarg + 5], false, lmp); - items.emplace_back(std::move(sphere)); - ++numobjs; - iarg += 6; - } else if (strcmp(arg[iarg], "cylinder") == 0) { - if (iarg + 9 > narg) utils::missing_cmd_args(FLERR, "fix graphics cylinder", error); - // clang-format off - CylinderItem cylinder{CYLINDER, 1, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, 0.0, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - -1, -1, -1, -1, -1, -1, -1}; - // clang-format on - cylinder.type = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); - if (strstr(arg[iarg + 2], "v_") == arg[iarg + 2]) { - varflag = 1; - cylinder.x1str = utils::strdup(arg[iarg + 2] + 2); - } else - cylinder.pos1[X] = utils::numeric(FLERR, arg[iarg + 2], false, lmp); - if (strstr(arg[iarg + 3], "v_") == arg[iarg + 3]) { - varflag = 1; - cylinder.y1str = utils::strdup(arg[iarg + 3] + 2); - } else - cylinder.pos1[Y] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); - if (strstr(arg[iarg + 4], "v_") == arg[iarg + 4]) { - varflag = 1; - cylinder.z1str = utils::strdup(arg[iarg + 4] + 2); - } else - cylinder.pos1[Z] = utils::numeric(FLERR, arg[iarg + 4], false, lmp); - if (strstr(arg[iarg + 5], "v_") == arg[iarg + 5]) { - varflag = 1; - cylinder.x2str = utils::strdup(arg[iarg + 5] + 2); - } else - cylinder.pos2[X] = utils::numeric(FLERR, arg[iarg + 5], false, lmp); - if (strstr(arg[iarg + 6], "v_") == arg[iarg + 6]) { - varflag = 1; - cylinder.y2str = utils::strdup(arg[iarg + 6] + 2); - } else - cylinder.pos2[Y] = utils::numeric(FLERR, arg[iarg + 6], false, lmp); - if (strstr(arg[iarg + 7], "v_") == arg[iarg + 7]) { - varflag = 1; - cylinder.z2str = utils::strdup(arg[iarg + 7] + 2); - } else - cylinder.pos2[Z] = utils::numeric(FLERR, arg[iarg + 7], false, lmp); - if (strstr(arg[iarg + 8], "v_") == arg[iarg + 8]) { - varflag = 1; - cylinder.dstr = utils::strdup(arg[iarg + 8] + 2); - } else - cylinder.diameter = 2.0 * utils::numeric(FLERR, arg[iarg + 8], false, lmp); - items.emplace_back(std::move(cylinder)); - ++numobjs; - iarg += 9; - } else if (strcmp(arg[iarg], "arrow") == 0) { - if (iarg + 10 > narg) utils::missing_cmd_args(FLERR, "fix graphics arrow", error); - // clang-format off - ArrowItem arrow{ARROW, 1, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, 0.0, 0.1, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - -1, -1, -1, -1, -1, -1, -1}; - // clang-format on - arrow.type = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); - if (strstr(arg[iarg + 2], "v_") == arg[iarg + 2]) { - varflag = 1; - arrow.x1str = utils::strdup(arg[iarg + 2] + 2); - } else - arrow.tip[X] = utils::numeric(FLERR, arg[iarg + 2], false, lmp); - if (strstr(arg[iarg + 3], "v_") == arg[iarg + 3]) { - varflag = 1; - arrow.y1str = utils::strdup(arg[iarg + 3] + 2); - } else - arrow.tip[Y] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); - if (strstr(arg[iarg + 4], "v_") == arg[iarg + 4]) { - varflag = 1; - arrow.z1str = utils::strdup(arg[iarg + 4] + 2); - } else - arrow.tip[Z] = utils::numeric(FLERR, arg[iarg + 4], false, lmp); - if (strstr(arg[iarg + 5], "v_") == arg[iarg + 5]) { - varflag = 1; - arrow.x2str = utils::strdup(arg[iarg + 5] + 2); - } else - arrow.bot[X] = utils::numeric(FLERR, arg[iarg + 5], false, lmp); - if (strstr(arg[iarg + 6], "v_") == arg[iarg + 6]) { - varflag = 1; - arrow.y2str = utils::strdup(arg[iarg + 6] + 2); - } else - arrow.bot[Y] = utils::numeric(FLERR, arg[iarg + 6], false, lmp); - if (strstr(arg[iarg + 7], "v_") == arg[iarg + 7]) { - varflag = 1; - arrow.z2str = utils::strdup(arg[iarg + 7] + 2); - } else - arrow.bot[Z] = utils::numeric(FLERR, arg[iarg + 7], false, lmp); - if (strstr(arg[iarg + 8], "v_") == arg[iarg + 8]) { - varflag = 1; - arrow.dstr = utils::strdup(arg[iarg + 8] + 2); - } else - arrow.diameter = 2.0 * utils::numeric(FLERR, arg[iarg + 8], false, lmp); - arrow.ratio = utils::numeric(FLERR, arg[iarg + 9], false, lmp); - if ((arrow.ratio < 0.1) || (arrow.ratio > 0.5)) - error->all(FLERR, iarg + 9, "Arrow tip ratio must be between 0.1 and 0.5"); - items.emplace_back(std::move(arrow)); - numobjs += 2; - iarg += 10; - } else if (strcmp(arg[iarg], "progbar") == 0) { - if (iarg + 11 > narg) utils::missing_cmd_args(FLERR, "fix graphics progbar", error); - // clang-format off - ProgbarItem progbar{PROGBAR, 1, 2, Y, 0, {0.0, 0.0, 0.0}, 0.0, 0.0, 0.0, nullptr, -1}; - // clang-format on - progbar.type1 = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); - progbar.type2 = utils::inumeric(FLERR, arg[iarg + 2], false, lmp); - if (strcmp(arg[iarg + 3], "x") == 0) { - progbar.dim = X; - } else if (strcmp(arg[iarg + 3], "y") == 0) { - progbar.dim = Y; - } else if (strcmp(arg[iarg + 3], "z") == 0) { - progbar.dim = Z; - } else { - error->all(FLERR, iarg + 3, "Unsupported progress bar dimension string {}", arg[iarg + 3]); - } - progbar.pos[X] = utils::numeric(FLERR, arg[iarg + 4], false, lmp); - progbar.pos[Y] = utils::numeric(FLERR, arg[iarg + 5], false, lmp); - progbar.pos[Z] = utils::numeric(FLERR, arg[iarg + 6], false, lmp); - progbar.length = utils::numeric(FLERR, arg[iarg + 7], false, lmp); - if ((progbar.length <= 0.0) || (progbar.length > 2.0 * domain->prd[progbar.dim])) - error->all(FLERR, iarg + 7, "Illegal progress bar length {}", arg[iarg + 7]); - progbar.diameter = 2.0 * utils::numeric(FLERR, arg[iarg + 8], false, lmp); - if (strstr(arg[iarg + 9], "v_") == arg[iarg + 9]) { - varflag = 1; - progbar.pstr = utils::strdup(arg[iarg + 9] + 2); - } else { - progbar.progress = utils::numeric(FLERR, arg[iarg + 9], false, lmp); - } - progbar.tics = utils::inumeric(FLERR, arg[iarg + 10], false, lmp); - if ((progbar.tics < 0) || (progbar.tics > 20)) - error->all(FLERR, iarg + 10, "Unsupported number of progress bar tics {}", arg[iarg + 10]); - items.emplace_back(std::move(progbar)); - numobjs += 2 + progbar.tics; - iarg += 11; - } else { - error->all(FLERR, iarg, "Unknown fix graphics keyword {}", arg[iarg]); - } - } - memory->create(imgobjs, numobjs, "fix_graphics:imgobjs"); - memory->create(imgparms, numobjs, 8, "fix_graphics:imgparms"); -} - -/* ---------------------------------------------------------------------- */ - -FixGraphics::~FixGraphics() -{ - for (auto &gi : items) { - switch (gi.style) { - case SPHERE: - delete[] gi.sphere.xstr; - delete[] gi.sphere.ystr; - delete[] gi.sphere.zstr; - delete[] gi.sphere.dstr; - break; - case CYLINDER: - delete[] gi.cylinder.x1str; - delete[] gi.cylinder.y1str; - delete[] gi.cylinder.z1str; - delete[] gi.cylinder.x2str; - delete[] gi.cylinder.y2str; - delete[] gi.cylinder.z2str; - delete[] gi.cylinder.dstr; - break; - case ARROW: - delete[] gi.arrow.x1str; - delete[] gi.arrow.y1str; - delete[] gi.arrow.z1str; - delete[] gi.arrow.x2str; - delete[] gi.arrow.y2str; - delete[] gi.arrow.z2str; - delete[] gi.arrow.dstr; - break; - case PROGBAR: - delete[] gi.progbar.pstr; - break; - default:; // do nothing - break; - } - } - - memory->destroy(imgobjs); - memory->destroy(imgparms); -} - -/* ---------------------------------------------------------------------- */ - -int FixGraphics::setmask() -{ - return END_OF_STEP; -} - -/* ---------------------------------------------------------------------- */ - -void FixGraphics::init() -{ - int n = 0; - for (auto &gi : items) { - if (gi.style == SPHERE) { - imgobjs[n] = DumpImage::SPHERE; - imgparms[n][0] = gi.sphere.type; - if (gi.sphere.xstr) { - int ivar = input->variable->find(gi.sphere.xstr); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.sphere.xstr); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.sphere.xstr); - gi.sphere.xvar = ivar; - } - if (gi.sphere.ystr) { - int ivar = input->variable->find(gi.sphere.ystr); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.sphere.ystr); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.sphere.ystr); - gi.sphere.yvar = ivar; - } - if (gi.sphere.zstr) { - int ivar = input->variable->find(gi.sphere.zstr); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.sphere.zstr); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.sphere.zstr); - gi.sphere.zvar = ivar; - } - if (gi.sphere.dstr) { - int ivar = input->variable->find(gi.sphere.dstr); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.sphere.dstr); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.sphere.dstr); - gi.sphere.dvar = ivar; - } - ++n; - } else if (gi.style == CYLINDER) { - imgobjs[n] = DumpImage::CYLINDER; - imgparms[n][0] = gi.cylinder.type; - if (gi.cylinder.x1str) { - int ivar = input->variable->find(gi.cylinder.x1str); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.cylinder.x1str); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.cylinder.x1str); - gi.cylinder.x1var = ivar; - } - if (gi.cylinder.y1str) { - int ivar = input->variable->find(gi.cylinder.y1str); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.cylinder.y1str); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.cylinder.y1str); - gi.cylinder.y1var = ivar; - } - if (gi.cylinder.z1str) { - int ivar = input->variable->find(gi.cylinder.z1str); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.cylinder.z1str); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.cylinder.z1str); - gi.cylinder.z1var = ivar; - } - if (gi.cylinder.x2str) { - int ivar = input->variable->find(gi.cylinder.x2str); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.cylinder.x2str); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.cylinder.x2str); - gi.cylinder.x2var = ivar; - } - if (gi.cylinder.y2str) { - int ivar = input->variable->find(gi.cylinder.y2str); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.cylinder.y2str); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.cylinder.y2str); - gi.cylinder.y2var = ivar; - } - if (gi.cylinder.z2str) { - int ivar = input->variable->find(gi.cylinder.z2str); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.cylinder.z2str); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.cylinder.z2str); - gi.cylinder.z2var = ivar; - } - if (gi.cylinder.dstr) { - int ivar = input->variable->find(gi.cylinder.dstr); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.cylinder.dstr); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.cylinder.dstr); - gi.cylinder.dvar = ivar; - } - ++n; - } else if (gi.style == ARROW) { - imgobjs[n] = DumpImage::CYLINDER; - imgparms[n][0] = gi.arrow.type; - if (gi.arrow.x1str) { - int ivar = input->variable->find(gi.arrow.x1str); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.arrow.x1str); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.arrow.x1str); - gi.arrow.x1var = ivar; - } - if (gi.arrow.y1str) { - int ivar = input->variable->find(gi.arrow.y1str); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.arrow.y1str); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.arrow.y1str); - gi.arrow.y1var = ivar; - } - if (gi.arrow.z1str) { - int ivar = input->variable->find(gi.arrow.z1str); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.arrow.z1str); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.arrow.z1str); - gi.arrow.z1var = ivar; - } - if (gi.arrow.x2str) { - int ivar = input->variable->find(gi.arrow.x2str); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.arrow.x2str); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.arrow.x2str); - gi.arrow.x2var = ivar; - } - if (gi.arrow.y2str) { - int ivar = input->variable->find(gi.arrow.y2str); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.arrow.y2str); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.arrow.y2str); - gi.arrow.y2var = ivar; - } - if (gi.arrow.z2str) { - int ivar = input->variable->find(gi.arrow.z2str); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.arrow.z2str); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.arrow.z2str); - gi.arrow.z2var = ivar; - } - if (gi.arrow.dstr) { - int ivar = input->variable->find(gi.arrow.dstr); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.arrow.dstr); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.arrow.dstr); - gi.arrow.dvar = ivar; - } - ++n; - imgobjs[n] = DumpImage::CYLINDER; - imgparms[n][0] = gi.arrow.type; - ++n; - } else if (gi.style == PROGBAR) { - imgobjs[n] = DumpImage::CYLINDER; - imgparms[n][0] = gi.progbar.type1; - imgparms[n][1] = gi.progbar.pos[X]; - imgparms[n][2] = gi.progbar.pos[Y]; - imgparms[n][3] = gi.progbar.pos[Z]; - imgparms[n][4] = gi.progbar.pos[X]; - imgparms[n][5] = gi.progbar.pos[Y]; - imgparms[n][6] = gi.progbar.pos[Z]; - imgparms[n][7] = gi.progbar.diameter; - switch (gi.progbar.dim) { - case X: - imgparms[n][1] -= 0.5 * gi.progbar.length; - imgparms[n][4] += 0.5 * gi.progbar.length; - break; - case Y: - imgparms[n][2] -= 0.5 * gi.progbar.length; - imgparms[n][5] += 0.5 * gi.progbar.length; - break; - case Z: - imgparms[n][3] -= 0.5 * gi.progbar.length; - imgparms[n][6] += 0.5 * gi.progbar.length; - break; - default:; // do nothing - } - ++n; - imgobjs[n] = DumpImage::CYLINDER; - imgparms[n][0] = gi.progbar.type2; - imgparms[n][1] = gi.progbar.pos[X]; - imgparms[n][2] = gi.progbar.pos[Y]; - imgparms[n][3] = gi.progbar.pos[Z]; - imgparms[n][4] = gi.progbar.pos[X]; - imgparms[n][5] = gi.progbar.pos[Y]; - imgparms[n][6] = gi.progbar.pos[Z]; - imgparms[n][7] = 0.75 * gi.progbar.diameter; - switch (gi.progbar.dim) { - case X: - imgparms[n][1] -= 0.5 * gi.progbar.length; - imgparms[n][4] -= 0.5 * gi.progbar.length; - imgparms[n][3] += 0.2 * gi.progbar.diameter; - imgparms[n][6] += 0.2 * gi.progbar.diameter; - break; - case Y: - imgparms[n][2] -= 0.5 * gi.progbar.length; - imgparms[n][5] -= 0.5 * gi.progbar.length; - imgparms[n][1] += 0.15 * gi.progbar.diameter; - imgparms[n][4] += 0.15 * gi.progbar.diameter; - break; - case Z: - imgparms[n][3] -= 0.5 * gi.progbar.length; - imgparms[n][6] -= 0.5 * gi.progbar.length; - imgparms[n][1] += 0.15 * gi.progbar.diameter; - imgparms[n][4] += 0.15 * gi.progbar.diameter; - break; - default: - break; - } - ++n; - double delta = gi.progbar.length / (double) (gi.progbar.tics - 1); - double lo = gi.progbar.pos[gi.progbar.dim] - 0.5 * gi.progbar.length; - for (int i = 0; i < gi.progbar.tics; ++i) { - imgobjs[n] = DumpImage::CYLINDER; - imgparms[n][0] = gi.progbar.type1; - imgparms[n][1] = gi.progbar.pos[X]; - imgparms[n][2] = gi.progbar.pos[Y]; - imgparms[n][3] = gi.progbar.pos[Z]; - imgparms[n][4] = gi.progbar.pos[X]; - imgparms[n][5] = gi.progbar.pos[Y]; - imgparms[n][6] = gi.progbar.pos[Z]; - imgparms[n][7] = 1.1 * gi.progbar.diameter; - switch (gi.progbar.dim) { - case X: - imgparms[n][1] = lo + delta * i - 0.05 * delta; - imgparms[n][4] = lo + delta * i + 0.05 * delta; - break; - case Y: - imgparms[n][2] = lo + delta * i - 0.05 * delta; - imgparms[n][5] = lo + delta * i + 0.05 * delta; - break; - case Z: - imgparms[n][3] = lo + delta * i - 0.05 * delta; - imgparms[n][6] = lo + delta * i + 0.05 * delta; - break; - } - ++n; - } - if (gi.progbar.pstr) { - int ivar = input->variable->find(gi.progbar.pstr); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix graphics does not exist", - gi.progbar.pstr); - if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR, Error::NOLASTLINE, - "Fix graphics variable {} is not equal-style variable", gi.progbar.pstr); - gi.progbar.pvar = ivar; - } - } - } - end_of_step(); -} - -/* ---------------------------------------------------------------------- */ - -void FixGraphics::end_of_step() -{ - // evaluate variable if necessary, wrap with clear/add - - if (varflag) modify->clearstep_compute(); - - int n = 0; - for (auto &gi : items) { - if (gi.style == SPHERE) { - if (gi.sphere.xstr) gi.sphere.pos[X] = input->variable->compute_equal(gi.sphere.xvar); - if (gi.sphere.ystr) gi.sphere.pos[Y] = input->variable->compute_equal(gi.sphere.yvar); - if (gi.sphere.zstr) gi.sphere.pos[Z] = input->variable->compute_equal(gi.sphere.zvar); - if (gi.sphere.dstr) gi.sphere.diameter = 2.0 * input->variable->compute_equal(gi.sphere.dvar); - imgparms[n][1] = gi.sphere.pos[X]; - imgparms[n][2] = gi.sphere.pos[Y]; - imgparms[n][3] = gi.sphere.pos[Z]; - imgparms[n][4] = gi.sphere.diameter; - ++n; - } else if (gi.style == CYLINDER) { - if (gi.cylinder.x1str) - gi.cylinder.pos1[X] = input->variable->compute_equal(gi.cylinder.x1var); - if (gi.cylinder.y1str) - gi.cylinder.pos1[Y] = input->variable->compute_equal(gi.cylinder.y1var); - if (gi.cylinder.z1str) - gi.cylinder.pos1[Z] = input->variable->compute_equal(gi.cylinder.z1var); - if (gi.cylinder.x2str) - gi.cylinder.pos2[X] = input->variable->compute_equal(gi.cylinder.x2var); - if (gi.cylinder.y2str) - gi.cylinder.pos2[Y] = input->variable->compute_equal(gi.cylinder.y2var); - if (gi.cylinder.z2str) - gi.cylinder.pos2[Z] = input->variable->compute_equal(gi.cylinder.z2var); - if (gi.cylinder.dstr) - gi.cylinder.diameter = 2.0 * input->variable->compute_equal(gi.cylinder.dvar); - imgparms[n][1] = gi.cylinder.pos1[X]; - imgparms[n][2] = gi.cylinder.pos1[Y]; - imgparms[n][3] = gi.cylinder.pos1[Z]; - imgparms[n][4] = gi.cylinder.pos2[X]; - imgparms[n][5] = gi.cylinder.pos2[Y]; - imgparms[n][6] = gi.cylinder.pos2[Z]; - imgparms[n][7] = gi.cylinder.diameter; - ++n; - } else if (gi.style == ARROW) { - if (gi.arrow.x1str) gi.arrow.tip[X] = input->variable->compute_equal(gi.arrow.x1var); - if (gi.arrow.y1str) gi.arrow.tip[Y] = input->variable->compute_equal(gi.arrow.y1var); - if (gi.arrow.z1str) gi.arrow.tip[Z] = input->variable->compute_equal(gi.arrow.z1var); - if (gi.arrow.x2str) gi.arrow.bot[X] = input->variable->compute_equal(gi.arrow.x2var); - if (gi.arrow.y2str) gi.arrow.bot[Y] = input->variable->compute_equal(gi.arrow.y2var); - if (gi.arrow.z2str) gi.arrow.bot[Z] = input->variable->compute_equal(gi.arrow.z2var); - if (gi.arrow.dstr) gi.arrow.diameter = 2.0 * input->variable->compute_equal(gi.arrow.dvar); - - double mid[3], vec[3]; - MathExtra::sub3(gi.arrow.bot, gi.arrow.tip, vec); - MathExtra::scaleadd3(gi.arrow.ratio, vec, gi.arrow.tip, mid); - imgparms[n][1] = gi.arrow.tip[X]; - imgparms[n][2] = gi.arrow.tip[Y]; - imgparms[n][3] = gi.arrow.tip[Z]; - imgparms[n][4] = mid[X]; - imgparms[n][5] = mid[Y]; - imgparms[n][6] = mid[Z]; - imgparms[n][7] = gi.arrow.diameter * (1.0 + 5.0 * gi.arrow.ratio); - ++n; - imgparms[n][1] = mid[X]; - imgparms[n][2] = mid[Y]; - imgparms[n][3] = mid[Z]; - imgparms[n][4] = gi.arrow.bot[X]; - imgparms[n][5] = gi.arrow.bot[Y]; - imgparms[n][6] = gi.arrow.bot[Z]; - imgparms[n][7] = gi.arrow.diameter; - ++n; - } else if (gi.style == PROGBAR) { - ++n; - if (gi.progbar.pstr) gi.progbar.progress = input->variable->compute_equal(gi.progbar.pvar); - // bracket into [0.0;1.0] rather than throwing an error for just a viz item - gi.progbar.progress = std::max(std::min(gi.progbar.progress, 1.0), 0.0); - switch (gi.progbar.dim) { - case X: - imgparms[n][1] = gi.progbar.pos[X] + (gi.progbar.progress - 0.5) * gi.progbar.length; - break; - case Y: - imgparms[n][2] = gi.progbar.pos[Y] + (gi.progbar.progress - 0.5) * gi.progbar.length; - break; - case Z: - imgparms[n][3] = gi.progbar.pos[Z] + (gi.progbar.progress - 0.5) * gi.progbar.length; - break; - default: - break; - } - ++n; - n += gi.progbar.tics; - } - } - if (varflag) modify->addstep_compute((update->ntimestep / nevery) * nevery + nevery); -} - -/* ---------------------------------------------------------------------- - provide graphics information to dump image -------------------------------------------------------------------------- */ - -int FixGraphics::image(int *&objs, double **&parms) -{ - objs = imgobjs; - parms = imgparms; - return numobjs; -} diff --git a/src/fix_indent.cpp b/src/fix_indent.cpp index 0d98d43ba6f..8ecd6e8a732 100644 --- a/src/fix_indent.cpp +++ b/src/fix_indent.cpp @@ -19,8 +19,8 @@ #include "atom.h" #include "domain.h" -#include "dump_image.h" #include "error.h" +#include "graphics.h" #include "input.h" #include "lattice.h" #include "math_extra.h" @@ -125,32 +125,66 @@ FixIndent::FixIndent(LAMMPS *lmp, int narg, char **arg) : // set up indenter visualization if (istyle == SPHERE) { - // one sphere object to draw - memory->create(imgobjs, 1, "fix_indent:imgobjs"); - memory->create(imgparms, 1, 5, "fix_indent:imgparms"); - imgobjs[0] = DumpImage::SPHERE; - imgparms[0][0] = 1; // use color of first atom type + if (domain->dimension == 2) { + // one cone object to draw in 2d + memory->create(imgobjs, 1, "fix_indent:imgobjs"); + memory->create(imgparms, 1, 10, "fix_indent:imgparms"); + imgobjs[0] = Graphics::CONE; + imgparms[0][0] = 1; // use color of first atom type + imgparms[0][9] = Graphics::CONE_TOP; // draw only the top circle + } else { + // one sphere object to draw in 3d + memory->create(imgobjs, 1, "fix_indent:imgobjs"); + memory->create(imgparms, 1, 5, "fix_indent:imgparms"); + imgobjs[0] = Graphics::SPHERE; + imgparms[0][0] = 1; // use color of first atom type + } } else if (istyle == CYLINDER) { // one cylinder object to draw memory->create(imgobjs, 1, "fix_indent:imgobjs"); memory->create(imgparms, 1, 8, "fix_indent:imgparms"); - imgobjs[0] = DumpImage::CYLINDER; + imgobjs[0] = Graphics::CYLINDER; imgparms[0][0] = 1; // use color of first atom type + } else if (istyle == CONE) { + if ((domain->dimension == 2) && (cdim != 2)) { + // three triangle objects to draw in 2d for x and y direction + memory->create(imgobjs, 3, "fix_indent:imgobjs"); + memory->create(imgparms, 3, 10, "fix_indent:imgparms"); + imgobjs[0] = Graphics::TRIANGLE; + imgobjs[1] = Graphics::TRIANGLE; + imgobjs[2] = Graphics::TRIANGLE; + // use color of first atom type with color style "type" or "element" + // use color style "const" and dump_modify fcolor to override + imgparms[0][0] = 1; + imgparms[1][0] = 1; + imgparms[2][0] = 1; + } else { + // one cone object to draw in 3d or if 2d and z-direction + memory->create(imgobjs, 1, "fix_indent:imgobjs"); + memory->create(imgparms, 1, 10, "fix_indent:imgparms"); + imgobjs[0] = Graphics::CONE; + imgparms[0][0] = 1; // use color of first atom type + imgparms[0][9] = Graphics::CONE_ALL; // caps on both sides + } } else if (istyle == PLANE) { if (domain->dimension == 2) { // one cylinder object to draw in 2d memory->create(imgobjs, 1, "fix_indent:imgobjs"); memory->create(imgparms, 1, 8, "fix_indent:imgparms"); - imgobjs[0] = DumpImage::CYLINDER; - imgparms[0][0] = 1; // use color of first atom type + imgobjs[0] = Graphics::CYLINDER; + // use color of first atom type with color style "type" or "element" + // use color style "const" and dump_modify fcolor to override + imgparms[0][0] = 1; } else { // two triangle objects to draw in 3d memory->create(imgobjs, 2, "fix_indent:imgobjs"); memory->create(imgparms, 2, 10, "fix_indent:imgparms"); - imgobjs[0] = DumpImage::TRIANGLE; - imgobjs[1] = DumpImage::TRIANGLE; - imgparms[0][0] = 1; // use color of first atom type by default - imgparms[1][0] = 1; // use color of first atom type by default + imgobjs[0] = Graphics::TRIANGLE; + imgobjs[1] = Graphics::TRIANGLE; + // use color of first atom type with color style "type" or "element" + // use color style "const" and dump_modify fcolor to override + imgparms[0][0] = 1; + imgparms[1][0] = 1; } } } @@ -338,8 +372,17 @@ void FixIndent::post_force(int /*vflag*/) imgparms[0][1] = ctr[0]; imgparms[0][2] = ctr[1]; - imgparms[0][3] = ctr[2]; - imgparms[0][4] = 2.0 * radius; + if (domain->dimension == 2) { + imgparms[0][3] = -0.5; + imgparms[0][4] = ctr[0]; + imgparms[0][5] = ctr[1]; + imgparms[0][6] = 0.5; + imgparms[0][7] = radius; + imgparms[0][8] = radius; + } else { + imgparms[0][3] = ctr[2]; + imgparms[0][4] = 2.0 * radius; + } // cylindrical indenter @@ -459,6 +502,71 @@ void FixIndent::post_force(int /*vflag*/) } } + // store indenter object visualization parameters: positions of cone centers and radii + + if ((domain->dimension == 2) && (cdim != 2)) { + // three triangles + for (int i = 0; i < 3; ++i) { // z coordinate is always the same + imgparms[i][3] = 0.5; + imgparms[i][6] = 0.5; + imgparms[i][9] = 0.5; + } + // increase radii a little bit to avoid artifacts from triangles with zero edge length + if (cdim == 0) { + const double EPSILON = 0.0001 * domain->yprd; + imgparms[0][1] = lo; + imgparms[0][2] = ctr[1] - radiuslo - EPSILON; + imgparms[0][4] = hi; + imgparms[0][5] = ctr[1]; + imgparms[0][7] = hi; + imgparms[0][8] = ctr[1] - radiushi - EPSILON; + imgparms[1][1] = lo; + imgparms[1][2] = ctr[1] - radiuslo - EPSILON; + imgparms[1][4] = lo; + imgparms[1][5] = ctr[1] + radiuslo + EPSILON; + imgparms[1][7] = hi; + imgparms[1][8] = ctr[1]; + imgparms[2][1] = lo; + imgparms[2][2] = ctr[1] + radiuslo + EPSILON; + imgparms[2][4] = hi; + imgparms[2][5] = ctr[1] + radiushi + EPSILON; + imgparms[2][7] = hi; + imgparms[2][8] = ctr[1]; + } else { // if (cdim == 1) + const double EPSILON = 0.0001 * domain->xprd; + imgparms[0][1] = ctr[0] - radiuslo - EPSILON; + imgparms[0][2] = lo; + imgparms[0][4] = ctr[0]; + imgparms[0][5] = hi; + imgparms[0][7] = ctr[0] - radiushi - EPSILON; + imgparms[0][8] = hi; + imgparms[1][1] = ctr[0] - radiuslo - EPSILON; + imgparms[1][2] = lo; + imgparms[1][4] = ctr[0] + radiuslo + EPSILON; + imgparms[1][5] = lo; + imgparms[1][7] = ctr[0]; + imgparms[1][8] = hi; + imgparms[2][1] = ctr[0] + radiuslo + EPSILON; + imgparms[2][2] = lo; + imgparms[2][4] = ctr[0] + radiushi + EPSILON; + imgparms[2][5] = hi; + imgparms[2][7] = ctr[0]; + imgparms[2][8] = hi; + } + } else { + // one cone + ctr[cdim] = hi; + imgparms[0][1] = ctr[0]; + imgparms[0][2] = ctr[1]; + imgparms[0][3] = ctr[2]; + ctr[cdim] = lo; + imgparms[0][4] = ctr[0]; + imgparms[0][5] = ctr[1]; + imgparms[0][6] = ctr[2]; + imgparms[0][7] = radiuslo; + imgparms[0][8] = radiushi; + } + // planar indenter } else { // if (istyle == PLANE) @@ -1016,6 +1124,11 @@ int FixIndent::image(int *&objs, double **&parms) return 1; else if (istyle == CYLINDER) return 1; + else if (istyle == CONE) + if ((domain->dimension == 2) && (cdim != 2)) + return 3; + else + return 1; else if (istyle == PLANE) if (domain->dimension == 2) return 1; diff --git a/src/fix_wall.cpp b/src/fix_wall.cpp index 93ebc0c0fc4..e104714db88 100644 --- a/src/fix_wall.cpp +++ b/src/fix_wall.cpp @@ -14,8 +14,8 @@ #include "fix_wall.h" #include "domain.h" -#include "dump_image.h" #include "error.h" +#include "graphics.h" #include "input.h" #include "lattice.h" #include "memory.h" @@ -109,10 +109,10 @@ void FixWall::update_image_plane(int m, int which, double coord, double **imgpar break; case ZLO: // fallthrough case ZHI: - imgparms[2 * m][1] = domain->boxlo[0]; + imgparms[2 * m][1] = domain->boxhi[0]; imgparms[2 * m][2] = domain->boxlo[1]; imgparms[2 * m][3] = coord; - imgparms[2 * m][4] = domain->boxhi[0]; + imgparms[2 * m][4] = domain->boxlo[0]; imgparms[2 * m][5] = domain->boxlo[1]; imgparms[2 * m][6] = coord; imgparms[2 * m][7] = domain->boxlo[0]; @@ -121,11 +121,11 @@ void FixWall::update_image_plane(int m, int which, double coord, double **imgpar imgparms[2 * m + 1][1] = domain->boxhi[0]; imgparms[2 * m + 1][2] = domain->boxhi[1]; imgparms[2 * m + 1][3] = coord; - imgparms[2 * m + 1][4] = domain->boxlo[0]; - imgparms[2 * m + 1][5] = domain->boxhi[1]; + imgparms[2 * m + 1][4] = domain->boxhi[0]; + imgparms[2 * m + 1][5] = domain->boxlo[1]; imgparms[2 * m + 1][6] = coord; - imgparms[2 * m + 1][7] = domain->boxhi[0]; - imgparms[2 * m + 1][8] = domain->boxlo[1]; + imgparms[2 * m + 1][7] = domain->boxlo[0]; + imgparms[2 * m + 1][8] = domain->boxhi[1]; imgparms[2 * m + 1][9] = coord; break; } @@ -342,7 +342,7 @@ FixWall::FixWall(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg), nwall memory->create(imgobjs, nwall, "fix_wall:imgobjs"); memory->create(imgparms, nwall, 8, "fix_wall:imgparms"); for (int m = 0; m < nwall; ++m) { - imgobjs[m] = DumpImage::CYLINDER; + imgobjs[m] = Graphics::CYLINDER; imgparms[m][0] = 1; // use color of first atom type by default } } else { @@ -350,8 +350,8 @@ FixWall::FixWall(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg), nwall memory->create(imgobjs, 2 * nwall, "fix_wall:imgobjs"); memory->create(imgparms, 2 * nwall, 10, "fix_wall:imgparms"); for (int m = 0; m < nwall; ++m) { - imgobjs[2 * m] = DumpImage::TRIANGLE; - imgobjs[2 * m + 1] = DumpImage::TRIANGLE; + imgobjs[2 * m] = Graphics::TRIANGLE; + imgobjs[2 * m + 1] = Graphics::TRIANGLE; imgparms[2 * m][0] = 1; // use color of first atom type by default imgparms[2 * m + 1][0] = 1; // use color of first atom type by default } diff --git a/src/fix_wall.h b/src/fix_wall.h index 682d9785e3e..f863ccf182b 100644 --- a/src/fix_wall.h +++ b/src/fix_wall.h @@ -60,6 +60,7 @@ class FixWall : public Fix { int eflag; // per-wall flag for energy summation int ilevel_respa; int fldflag; + int *imgobjs; double **imgparms; }; diff --git a/src/fix_wall_reflect.cpp b/src/fix_wall_reflect.cpp index 43b9bd51f44..ccb61909897 100644 --- a/src/fix_wall_reflect.cpp +++ b/src/fix_wall_reflect.cpp @@ -17,9 +17,9 @@ #include "atom.h" #include "comm.h" #include "domain.h" -#include "dump_image.h" #include "error.h" #include "fix_wall.h" +#include "graphics.h" #include "input.h" #include "lattice.h" #include "memory.h" @@ -149,7 +149,7 @@ FixWallReflect::FixWallReflect(LAMMPS *lmp, int narg, char **arg) : memory->create(imgobjs, nwall, "fix_wall_reflect:imgobjs"); memory->create(imgparms, nwall, 8, "fix_wall_reflect:imgparms"); for (int m = 0; m < nwall; ++m) { - imgobjs[m] = DumpImage::CYLINDER; + imgobjs[m] = Graphics::CYLINDER; imgparms[m][0] = 1; // use color of first atom type by default } } else { @@ -157,8 +157,8 @@ FixWallReflect::FixWallReflect(LAMMPS *lmp, int narg, char **arg) : memory->create(imgobjs, 2 * nwall, "fix_wall_reflect:imgobjs"); memory->create(imgparms, 2 * nwall, 10, "fix_wall_reflect:imgparms"); for (int m = 0; m < nwall; ++m) { - imgobjs[2 * m] = DumpImage::TRIANGLE; - imgobjs[2 * m + 1] = DumpImage::TRIANGLE; + imgobjs[2 * m] = Graphics::TRIANGLE; + imgobjs[2 * m + 1] = Graphics::TRIANGLE; imgparms[2 * m][0] = 1; // use color of first atom type by default imgparms[2 * m + 1][0] = 1; // use color of first atom type by default } diff --git a/src/graphics.h b/src/graphics.h new file mode 100644 index 00000000000..e5a14e011a4 --- /dev/null +++ b/src/graphics.h @@ -0,0 +1,43 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef LMP_GRAPHICS_H +#define LMP_GRAPHICS_H + +// common definitions and declarations for graphics support in LAMMPS + +namespace LAMMPS_NS { +namespace Graphics { + enum { + NONE, + SPHERE, // a single sphere with radius provided + LINE, // a cylinder with diameter given through fflag2 + TRI, // a surface mesh as triangles or cylinder mesh based on fflag1, fflag2 sets diameter + CYLINDER, // a cylinder with diameter given by fix, fflag1 choose caps, fflag2 adjusts diameter + TRIANGLE, // a regular triangle, no settings apply + BOND, // two connected cylinders with bond diameter, colored by atom types, fflag1 sets cap + ARROW, // a cylinder with a conical tip and a flat cap at the bottom + CONE, // a truncated cone with flat caps, fflag1 sets caps + PIXMAP // a pointer to a pixmap buffer at x,y,z location + }; // used by some Body and Fix child classes + + // definitions for rendering caps and sides of a truncated cone + enum { + CONE_TOP = 1 << 0, // draw top cap of cone/cylinder + CONE_BOT = 1 << 1, // draw bottom cap of cone/cylinder + CONE_SIDE = 1 << 2, // draw side of cone/cylinder + CONE_ALL = CONE_TOP | CONE_BOT | CONE_SIDE // all of the above + }; +} // namespace Graphics +} // namespace LAMMPS_NS +#endif diff --git a/src/input.cpp b/src/input.cpp index 89e951f4f67..0914132a5de 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -599,9 +599,16 @@ void Input::substitute(char *&str, char *&str2, int &max, int &max2, int flag) while (*ptr) { + // if this is not the first character and the character before ptr is a backslash + // do *not* expand as variable but overwrite the backslash with the $ + + if ((*ptr == '$') && (ptr > str) && (ptr[- 1] == '\\')) { + + ptr2[-1] = *ptr++; + // variable substitution - if (*ptr == '$') { + } else if (*ptr == '$') { // value = ptr to expanded variable // variable name between curly braces, e.g. ${a} diff --git a/src/lammps.cpp b/src/lammps.cpp index f6611e564d8..33fd815a561 100644 --- a/src/lammps.cpp +++ b/src/lammps.cpp @@ -143,9 +143,13 @@ LAMMPS::LAMMPS(int narg, char **arg, MPI_Comm communicator) : num_ver = utils::date2num(version); restart_ver = -1; - // append git descriptor info to update string when compiling development or maintenance version - std::string update_string = UPDATE_STRING; // NOLINT + + // increment the version number for development branch + // so that it is larger than that of the release version + if (update_string == " - Development") ++num_ver; + + // append git descriptor info to update string when compiling development or maintenance version if (has_git_info() && ((update_string == " - Development") || (update_string == " - Maintenance"))) update_string += fmt::format(" - {}", git_descriptor()); diff --git a/src/library.cpp b/src/library.cpp index 4c5d5a2f22c..ae983839347 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -1512,8 +1512,14 @@ internally by the :doc:`Fortran interface ` and are not likely to be us - 1 if the atom style includes per-atom masses, 0 if there are per-type masses. See :doc:`atom_style`. * - radius_flag - 1 if the atom style includes a per-atom radius. See :doc:`atom_style`. + * - body_flag + - 1 if the atom style describes body particles. See :doc:`atom_style`. * - ellipsoid_flag - 1 if the atom style describes extended particles that may be ellipsoidal. See :doc:`atom_style`. + * - line_flag + - 1 if the atom style describes line particles. See :doc:`atom_style`. + * - tri_flag + - 1 if the atom style describes tri particles. See :doc:`atom_style`. * - omega_flag - 1 if the atom style can store per-atom rotational velocities. See :doc:`atom_style`. * - torque_flag @@ -1617,7 +1623,11 @@ int lammps_extract_setting(void *handle, const char *keyword) if (strcmp(keyword,"rmass_flag") == 0) return lmp->atom->rmass_flag; if (strcmp(keyword,"radius_flag") == 0) return lmp->atom->radius_flag; + if (strcmp(keyword,"body_flag") == 0) return lmp->atom->body_flag; if (strcmp(keyword,"ellipsoid_flag") == 0) return lmp->atom->ellipsoid_flag; + if (strcmp(keyword,"line_flag") == 0) return lmp->atom->line_flag; + if (strcmp(keyword,"tri_flag") == 0) return lmp->atom->tri_flag; + if (strcmp(keyword,"omega_flag") == 0) return lmp->atom->omega_flag; if (strcmp(keyword,"torque_flag") == 0) return lmp->atom->torque_flag; if (strcmp(keyword,"angmom_flag") == 0) return lmp->atom->angmom_flag; diff --git a/src/platform.cpp b/src/platform.cpp index e57c8fa4e01..d2546ac37d3 100644 --- a/src/platform.cpp +++ b/src/platform.cpp @@ -22,10 +22,13 @@ #include "utils.h" #include +#include +#include #include #include #include #include +#include #include //////////////////////////////////////////////////////////////////////// @@ -74,10 +77,6 @@ //////////////////////////////////////////////////////////////////////// -#include -#include -#include - /* ------------------------------------------------------------------ */ namespace { /// Struct for listing on-the-fly compression/decompression commands @@ -124,6 +123,9 @@ const compress_info &find_compress_type(const std::string &file) // set reference time stamp during executable/library init. // should provide better resolution than using epoch, if the system clock supports it. auto initial_time = std::chrono::steady_clock::now(); + +// same for file time stamps where we use the current working directory as reference +auto initial_file_time = std::filesystem::last_write_time("."); } // namespace using namespace LAMMPS_NS; @@ -934,7 +936,8 @@ std::string platform::path_dirname(const std::string &path) #else if (dir == "") return {"."}; #endif - else return dir; + else + return dir; } /* ---------------------------------------------------------------------- @@ -1003,6 +1006,15 @@ bool platform::file_is_writable(const std::string &path) return false; } +/* ---------------------------------------------------------------------- + get file modification time since initial time stamp +------------------------------------------------------------------------- */ +double platform::file_write_time(const std::string &path) +{ + auto timediff = std::filesystem::last_write_time(path) - initial_file_time; + return std::chrono::duration(timediff).count(); +} + /* ---------------------------------------------------------------------- determine available disk space, if supported. Return -1 if not. ------------------------------------------------------------------------- */ diff --git a/src/platform.h b/src/platform.h index b3fa61de794..de91cfdfd53 100644 --- a/src/platform.h +++ b/src/platform.h @@ -371,6 +371,21 @@ namespace platform { bool file_is_writable(const std::string &path); + /*! Report a time stamp when a file was last written to + * + * For increased accuracy and portability, the time stamp is relative + * to an arbitrary offset created when loading the LAMMPS library or + * launching the executable. This allows to report the time difference + * as a floating point number and not some platform or architecture + * specific type requiring to include additional headers. + * + * When two timestamps are the same, the file has not been written to. + * + * \param path file path + * \return time stamp when file was last written to */ + + double file_write_time(const std::string &path); + /*! Return free disk space in bytes of file system pointed to by path * * Returns -1.0 if the path is invalid or free space reporting not supported. diff --git a/src/procmap.cpp b/src/procmap.cpp index 4472f5da1ee..9b51e69df07 100644 --- a/src/procmap.cpp +++ b/src/procmap.cpp @@ -678,7 +678,7 @@ void ProcMap::output(char *file, int *procgrid, int ***grid2proc) // polled comm of grid mapping info from each proc to proc 0 - int tmp; + int tmp = 0; int vec[6]; char procname[MPI_MAX_PROCESSOR_NAME+1]; diff --git a/src/region_plane.h b/src/region_plane.h index 118bd887931..4ff04221389 100644 --- a/src/region_plane.h +++ b/src/region_plane.h @@ -36,10 +36,10 @@ class RegPlane : public Region { int surface_exterior(double *, double) override; void shape_update() override; - private: double xp, yp, zp; double normal[3]; + private: int xstyle, xvar; int ystyle, yvar; int zstyle, zvar; diff --git a/src/set.cpp b/src/set.cpp index b20f26198c1..4a780e53e00 100644 --- a/src/set.cpp +++ b/src/set.cpp @@ -1656,7 +1656,7 @@ void Set::process_image(int &iarg, int narg, char **arg, Action *action) } } - if (strcmp(arg[iarg+1],"NULL") == 0) action->ivalue6 = 0; + if (strcmp(arg[iarg+3],"NULL") == 0) action->ivalue6 = 0; else { action->ivalue6 = 1; if (utils::strmatch(arg[iarg+3],"^v_")) { diff --git a/src/variable.cpp b/src/variable.cpp index e5c8f3a01c3..afe3b840204 100644 --- a/src/variable.cpp +++ b/src/variable.cpp @@ -1148,8 +1148,10 @@ char *Variable::retrieve(const char *name) double Variable::compute_equal(int ivar) { - if (eval_in_progress[ivar]) - print_var_error(FLERR,"has a circular dependency",ivar); + // do nothing for out of range index + if ((ivar < 0) || (ivar >= nvar)) return 0.0; + + if (eval_in_progress[ivar]) print_var_error(FLERR,"has a circular dependency",ivar); eval_in_progress[ivar] = 1; @@ -1192,8 +1194,10 @@ void Variable::compute_atom(int ivar, int igroup, double *result, int stride, in Tree *tree = nullptr; double *vstore; - if (eval_in_progress[ivar]) - print_var_error(FLERR,"has a circular dependency",ivar); + // index out of range. do nothing. + if ((ivar < 0) || (ivar >= maxvar)) return; + + if (eval_in_progress[ivar]) print_var_error(FLERR, "has a circular dependency",ivar); eval_in_progress[ivar] = 1; @@ -1264,6 +1268,10 @@ int Variable::compute_vector(int ivar, double **result) { Tree *tree = nullptr; + // index is out-of-range. do nothing + + if ((ivar < 0) || (ivar >= nvar)) return 0; + // if vector is not dynamic, just return stored values if (!vecs[ivar].dynamic) { @@ -1280,8 +1288,7 @@ int Variable::compute_vector(int ivar, double **result) // evaluate vector variable afresh - if (eval_in_progress[ivar]) - print_var_error(FLERR,"has a circular dependency",ivar); + if (eval_in_progress[ivar]) print_var_error(FLERR,"has a circular dependency",ivar); eval_in_progress[ivar] = 1; @@ -1289,10 +1296,8 @@ int Variable::compute_vector(int ivar, double **result) evaluate(data[ivar][0],&tree,ivar); collapse_tree(tree); int nlen = size_tree_vector(tree); - if (nlen == 0) - print_var_error(FLERR,"Vector-style variable has zero length",ivar); - if (nlen < 0) - print_var_error(FLERR,"Inconsistent lengths in vector-style variable",ivar); + if (nlen == 0) print_var_error(FLERR,"Vector-style variable has zero length",ivar); + if (nlen < 0) print_var_error(FLERR,"Inconsistent lengths in vector-style variable",ivar); // (re)allocate space for results if necessary @@ -4693,8 +4698,7 @@ int Variable::special_function(const std::string &word, char *contents, Tree **t print_var_error(FLERR,"Invalid special function in variable formula",ivar); if (style[ivar] != VECTOR) print_var_error(FLERR,"Mis-matched special function variable in variable formula",ivar); - if (eval_in_progress[ivar]) - print_var_error(FLERR,"has a circular dependency",ivar); + if (eval_in_progress[ivar]) print_var_error(FLERR,"has a circular dependency",ivar); double *vec; nvec = compute_vector(ivar,&vec); diff --git a/src/write_data.cpp b/src/write_data.cpp index 98b2fad6572..9f03d876f81 100644 --- a/src/write_data.cpp +++ b/src/write_data.cpp @@ -427,7 +427,8 @@ void WriteData::atoms() // proc 0 pings each proc, receives its chunk, writes to file // all other procs wait for ping, send their chunk to proc 0 - int tmp,recvrow; + int tmp = 0; + int recvrow; if (comm->me == 0) { MPI_Status status; @@ -480,7 +481,8 @@ void WriteData::velocities() // proc 0 pings each proc, receives its chunk, writes to file // all other procs wait for ping, send their chunk to proc 0 - int tmp,recvrow; + int tmp = 0; + int recvrow; if (comm->me == 0) { MPI_Status status; @@ -533,7 +535,8 @@ void WriteData::bonds() // proc 0 pings each proc, receives its chunk, writes to file // all other procs wait for ping, send their chunk to proc 0 - int tmp,recvrow; + int tmp = 0; + int recvrow; int index = 1; if (comm->me == 0) { @@ -588,7 +591,8 @@ void WriteData::angles() // proc 0 pings each proc, receives its chunk, writes to file // all other procs wait for ping, send their chunk to proc 0 - int tmp,recvrow; + int tmp = 0; + int recvrow; int index = 1; if (comm->me == 0) { @@ -643,7 +647,8 @@ void WriteData::dihedrals() // proc 0 pings each proc, receives its chunk, writes to file // all other procs wait for ping, send their chunk to proc 0 - int tmp,recvrow; + int tmp = 0; + int recvrow; int index = 1; if (comm->me == 0) { @@ -698,7 +703,8 @@ void WriteData::impropers() // proc 0 pings each proc, receives its chunk, writes to file // all other procs wait for ping, send their chunk to proc 0 - int tmp,recvrow; + int tmp = 0; + int recvrow; int index = 1; if (comm->me == 0) { @@ -753,7 +759,7 @@ void WriteData::bonus(int flag) // proc 0 pings each proc, receives its chunk, writes to file // all other procs wait for ping, send their chunk to proc 0 - int tmp; + int tmp = 0; if (comm->me == 0) { MPI_Status status; @@ -809,7 +815,8 @@ void WriteData::fix(Fix *ifix, int mth) // proc 0 pings each proc, receives its chunk, writes to file // all other procs wait for ping, send their chunk to proc 0 - int tmp,recvrow; + int tmp = 0; + int recvrow; int index = 1; if (comm->me == 0) { diff --git a/src/write_dump.cpp b/src/write_dump.cpp index d7f5c9925bc..afc7f42f6cb 100644 --- a/src/write_dump.cpp +++ b/src/write_dump.cpp @@ -19,8 +19,6 @@ #include "comm.h" #include "dump.h" -#include "dump_cfg.h" -#include "dump_image.h" #include "error.h" #include "exceptions.h" #include "output.h" @@ -83,8 +81,8 @@ void WriteDump::command(int narg, char **arg) // write out one frame and then delete the dump again // set multifile_override for DumpImage so that filename needs no "*" - if (strcmp(arg[1], "image") == 0) (dynamic_cast(dump))->multifile_override = 1; - if (strcmp(arg[1], "cfg") == 0) (dynamic_cast(dump))->multifile_override = 1; + if (strcmp(arg[1], "image") == 0) dump->multifile_override = 1; + if (strcmp(arg[1], "cfg") == 0) dump->multifile_override = 1; if ((update->first_update == 0) && (comm->me == 0) && (noinitwarn == 0)) error->warning(FLERR, "Calling write_dump before a full system init"); diff --git a/src/write_restart.cpp b/src/write_restart.cpp index 80455107935..7614ba6acd5 100644 --- a/src/write_restart.cpp +++ b/src/write_restart.cpp @@ -343,7 +343,8 @@ void WriteRestart::write(const std::string &file) // ping each proc in my cluster, receive its data, write data to file // else wait for ping from fileproc, send my data to fileproc - int tmp,recv_size; + int tmp = 0; + int recv_size; if (filewriter) { MPI_Status status; diff --git a/unittest/cplusplus/test_input_class.cpp b/unittest/cplusplus/test_input_class.cpp index 91cc2e6ec0c..5a8833cca22 100644 --- a/unittest/cplusplus/test_input_class.cpp +++ b/unittest/cplusplus/test_input_class.cpp @@ -11,7 +11,7 @@ #include "gtest/gtest.h" const char *demo_input[] = {"region box block 0 $x 0 2 0 2", "create_box 1 box", - "create_atoms 1 single 1.0 1.0 ${zpos}"}; + "create_atoms 1 single 1.0 1.0 ${zpos}", "print \\${zpos}"}; const char *cont_input[] = {"create_atoms 1 single &", "0.2 0.1 0.1"}; namespace LAMMPS_NS { @@ -102,6 +102,11 @@ TEST_F(Input_commands, substitute) strcpy(string, demo_input[2]); lmp->input->substitute(string, scratch, nstring, nscratch, 0); EXPECT_STREQ(string, "create_atoms 1 single 1.0 1.0 1.5"); + + strcpy(string, demo_input[3]); + lmp->input->substitute(string, scratch, nstring, nscratch, 0); + EXPECT_STREQ(string, "print ${zpos}"); + lmp->memory->destroy(string); lmp->memory->destroy(scratch); }; diff --git a/unittest/formats/test_file_operations.cpp b/unittest/formats/test_file_operations.cpp index 331489d0f86..3d6ab281fc8 100644 --- a/unittest/formats/test_file_operations.cpp +++ b/unittest/formats/test_file_operations.cpp @@ -353,6 +353,9 @@ TEST_F(FileOperationsTest, write_restart) command("change_box all triclinic"); command("write_restart triclinic.restart"); END_HIDE_OUTPUT(); + // increment restart version if it differs by 1, + // i.e. it was written by a development version + if (lmp->num_ver - lmp->restart_ver == 1) lmp->restart_ver++; ASSERT_EQ(lmp->restart_ver, lmp->num_ver); ASSERT_EQ(lmp->atom->natoms, 1); ASSERT_EQ(lmp->update->ntimestep, 333); @@ -367,6 +370,9 @@ TEST_F(FileOperationsTest, write_restart) BEGIN_HIDE_OUTPUT(); command("read_restart triclinic.restart"); END_HIDE_OUTPUT(); + // increment restart version if it differs by 1, + // i.e. it was written by a development version + if (lmp->num_ver - lmp->restart_ver == 1) lmp->restart_ver++; ASSERT_EQ(lmp->restart_ver, lmp->num_ver); ASSERT_EQ(lmp->atom->natoms, 1); ASSERT_EQ(lmp->update->ntimestep, 333); diff --git a/unittest/utils/test_platform.cpp b/unittest/utils/test_platform.cpp index 5a4207b995d..20a7853770b 100644 --- a/unittest/utils/test_platform.cpp +++ b/unittest/utils/test_platform.cpp @@ -376,6 +376,32 @@ TEST(Platform, file_is_readable) #endif } +TEST(Platform, file_write_time) +{ + platform::unlink("file_is_not_modified.txt"); + platform::unlink("file_is_modified.txt"); + FILE *fp = fopen("file_is_not_modified.txt", "w"); + fputs("some text\n", fp); + fclose(fp); + fp = fopen("file_is_modified.txt", "w"); + fputs("some text\n", fp); + fclose(fp); + + auto ref_not_modified = platform::file_write_time("file_is_not_modified.txt"); + auto ref_modified = platform::file_write_time("file_is_modified.txt"); + + platform::usleep(1000000); + fp = fopen("file_is_modified.txt", "w"); + fputs("some text\n", fp); + fclose(fp); + + EXPECT_EQ(ref_not_modified, platform::file_write_time("file_is_not_modified.txt")); + EXPECT_NE(ref_modified, platform::file_write_time("file_is_modified.txt")); + + platform::unlink("file_is_not_modified.txt"); + platform::unlink("file_is_modified.txt"); +} + TEST(Platform, has_compress_extension) { ASSERT_FALSE(platform::has_compress_extension("dummy"));